2009-10-31 14 views
493

La clase ExpandoObject que se agrega a .NET 4 le permite establecer arbitrariamente propiedades en un objeto en tiempo de ejecución.¿Cuáles son los verdaderos beneficios de ExpandoObject?

¿Hay alguna ventaja en esto con el uso de Dictionary<string,object>, o realmente incluso en Hashtable? Por lo que puedo decir, esto no es más que una tabla hash a la que se puede acceder con una sintaxis un poco más sucinta.

Por ejemplo, ¿por qué es esto:

dynamic obj = new ExpandoObject(); 
obj.MyInt = 3; 
obj.MyString = "Foo"; 
Console.WriteLine(obj.MyString); 

realmente mejor, o sustancialmente diferente, que:

var obj = new Dictionary<string, object>(); 
obj["MyInt"] = 3; 
obj["MyString"] = "Foo"; 

Console.WriteLine(obj["MyString"]); 

Lo reales ventajas se obtienen mediante el uso de ExpandoObject en lugar de usar un arbitraria tipo de diccionario, además de no ser obvio que está utilizando un tipo que se determinará en tiempo de ejecución.

Respuesta

589

Desde que escribí el artículo de MSDN al que se refiere, creo que tengo que responder éste.

Primero, anticipé esta pregunta y es por eso que escribí una publicación de blog que muestra un caso de uso más o menos real para ExpandoObject: Dynamic in C# 4.0: Introducing the ExpandoObject.

En breve, ExpandoObject puede ayudarlo a crear objetos jerárquicos complejos. Por ejemplo, imagine que tiene un diccionario dentro de un diccionario:

Dictionary<String, object> dict = new Dictionary<string, object>(); 
Dictionary<String, object> address = new Dictionary<string,object>(); 
dict["Address"] = address; 
address["State"] = "WA"; 
Console.WriteLine(((Dictionary<string,object>)dict["Address"])["State"]); 

Cuanto más profunda es la jerarquía, el más feo es el código. Con ExpandoObject, se mantiene elegante y legible.

dynamic expando = new ExpandoObject(); 
expando.Address = new ExpandoObject(); 
expando.Address.State = "WA"; 
Console.WriteLine(expando.Address.State); 

En segundo lugar, como ya se señaló, ExpandoObject implementa la interfaz INotifyPropertyChanged que le da más control sobre las propiedades que un diccionario.

Por último, se puede añadir eventos a ExpandoObject como aquí:

class Program 
{ 

    static void Main(string[] args) 
    { 
     dynamic d = new ExpandoObject(); 

     // Initialize the event to null (meaning no handlers) 
     d.MyEvent = null; 

     // Add some handlers 
     d.MyEvent += new EventHandler(OnMyEvent); 
     d.MyEvent += new EventHandler(OnMyEvent2); 

     // Fire the event 
     EventHandler e = d.MyEvent; 

     if (e != null) 
     { 
      e(d, new EventArgs()); 
     } 

     // We could also fire it with... 
     //  d.MyEvent(d, new EventArgs()); 

     // ...if we knew for sure that the event is non-null. 
    } 

    static void OnMyEvent(object sender, EventArgs e) 
    { 
     Console.WriteLine("OnMyEvent fired by: {0}", sender); 
    } 

    static void OnMyEvent2(object sender, EventArgs e) 
    { 
     Console.WriteLine("OnMyEvent2 fired by: {0}", sender); 
    } 
} 
+42

Interesante. Gracias por la información re: eventos. Esa fue una nueva para mí. –

+4

@ Reed Sí, los eventos aún no están documentados para ExpandoObject. Este ejemplo en particular surgió en la discusión en el blog. Probablemente lo agregue a la documentación más tarde. –

+11

@AlexandraRusina, ¿cómo sabe que es un evento cuando dices 'd.MyEvent = null;', ¿O no? – Shimmy

17

Todo se trata de la comodidad del programador. Me puedo imaginar escribiendo programas rápidos y sucios con este objeto.

+8

@J. Hendrix, no olvides que también dijo "sucio". Intellisense tiene su lado negativo, sin embargo, hace que la depuración y la captura de errores sean más fáciles. Personalmente todavía prefiero los tipos estáticos sobre los dinámicos a menos que tenga que lidiar con un caso raro (y siempre raro). – Phil

+0

+1 por conveniencia. Sin embargo, encuentro que los tipos anónimos pueden ser igualmente convenientes como una bolsa de propiedades simple y simplemente mejor por su estática. – nawfal

+0

No me gustaría usarlo en el código de producción, pero es muy conveniente en el código de prueba y puede hacer que se vea muy bonito. – Tobias

64

Una ventaja es para escenarios vinculantes. Las cuadrículas de datos y las redes de propiedades recogerán las propiedades dinámicas a través del sistema TypeDescriptor. Además, el enlace de datos WPF comprenderá las propiedades dinámicas, por lo que los controles WPF pueden vincularse a un objeto ExpandoObject más fácilmente que un diccionario.

La interoperabilidad con lenguajes dinámicos, que estarán esperando propiedades DLR en lugar de entradas de diccionario, también puede ser una consideración en algunos escenarios.

+14

+1 Databinding e Interop –

+5

[Parece que la unión de datos a objetos dinámicos está interrumpida] (https://connect.microsoft.com/VisualStudio/feedback/details/522119/databinding-to-dynamic-objects-is -broken). El usuario informante eisenbergeffect está aquí en SO y coordinador de caliburn.micro. @AlexandraRusina puede comentar sobre el estado del error y el estado "No arreglará" – surfmuggle

+0

Para los curiosos, actualmente puedo enlazar a 'Lista ' y 'IEnumerable ' usando WPF4 –

24

La interoperabilidad con otros idiomas fundada en el DLR es la razón número 1 que se me ocurre. No puede pasarles un Dictionary<string, object> ya que no es un IDynamicMetaObjectProvider. Otra ventaja adicional es que implementa INotifyPropertyChanged lo que significa que en el mundo de WPF de enlace de datos también tiene beneficios adicionales más allá de lo que Dictionary<K,V> puede proporcionarle.

12

Creo que tendrá un beneficio sintáctico, ya que no estará "falsificando" las propiedades añadidas dinámicamente mediante el uso de un diccionario.

Eso, y la interoperabilidad con lenguajes dinámicos, creo.

31

El beneficio real para mí es la unión a partir de los datos XAML totalmente sin esfuerzo:

public dynamic SomeData { get; set; } 

...

SomeData.WhatEver = "Yo Man!"; 

...

<TextBlock Text="{Binding SomeData.WhatEver}" /> 
4

Hay algunos casos en que esto es útil. Lo usaré para un shell Modularizado, por ejemplo. Cada módulo define su propio cuadro de diálogo de configuración en su configuración. Le proporciono un ExpandoObject como Datacontext y guardo los valores en mi configuración Storage. De esta forma, el escritor del diálogo de configuración solo tiene que vincularse a un valor y se crea y guarda automáticamente. (Y se proporciona al módulo para usar estas configuraciones, por supuesto)

Es simplemente más fácil de usar que un diccionario. Pero todos deberían saber que internamente es solo un diccionario.

Es como el azúcar sintáctico LINQ, pero a veces hace las cosas más fáciles.

Para responder a su pregunta directamente: es más fácil de escribir y más fácil de leer. Pero técnicamente es esencialmente un Dictionary<string,object> (Incluso puedes convertirlo en uno para enumerar los valores).

8

Es ejemplo de la gran MSDN article sobre el uso de ExpandoObject para la creación de tipos ad-hoc dinámicos para datos estructurados entrantes (es decir, XML, JSON).

También podemos asignar delegado de propiedad dinámica ExpandoObject 's:

dynamic person = new ExpandoObject(); 
person.FirstName = "Dino"; 
person.LastName = "Esposito"; 

person.GetFullName = (Func<String>)(() => { 
    return String.Format("{0}, {1}", 
    person.LastName, person.FirstName); 
}); 

var name = person.GetFullName(); 
Console.WriteLine(name); 

Por lo tanto, nos permite inyectar algo de lógica en objeto dinámico en tiempo de ejecución. Por lo tanto, junto con las expresiones lambda, closures, palabra clave dinámica y DynamicObject class, podemos introducir algunos elementos de programación funcional en nuestro código C#, que conocemos desde lenguajes dinámicos como JavaScript o PHP.

0
var obj = new Dictionary<string, object>; 
... 
Console.WriteLine(obj["MyString"]); 

creo que sólo funciona porque todo tiene un ToString(), de lo contrario tendría que conocer el tipo que era fundido y el 'objeto' de ese tipo.


Algunos de estos son útiles más a menudo que otros, estoy tratando de ser exhaustivo.

  1. Puede ser mucho más natural para acceder a una colección, en este caso, lo que es efectivamente un "diccionario", usando la notación punto más directa.

  2. Parece que esto podría usarse como una Tuple realmente agradable. Todavía puede llamar a sus miembros "Item1", "Item2", etc ... pero ahora no es necesario, también es mutable, a diferencia de un Tuple. Esto tiene el gran inconveniente de la falta de soporte intellisense.

  3. Puede sentirse incómodo con "nombres de miembros como cadenas", como es la sensación con el diccionario, puede sentir que es demasiado como "ejecutar cadenas", y puede dar lugar a codificaciones de convenciones de nombres, y tratar con el trabajo con morfemas y sílabas cuando el código intenta entender cómo usar miembros :-P

  4. ¿Se puede asignar un valor a un ExpandoObject o solo a sus miembros? Compare y contraste con dinámico/dinámico [], use el que mejor se adapte a sus necesidades.

  5. No creo que dynamic/dynamic [] funcione en un bucle foreach, tienes que usar var, pero posiblemente puedas usar ExpandoObject.

  6. No puede usar el elemento dinámico como un miembro de datos en una clase, quizás porque es al menos algo así como una palabra clave, con suerte puede hacerlo con ExpandoObject.

  7. Espero que "es" un ExpandoObject, podría ser útil para etiquetar cosas muy genéricas aparte, con un código que lo diferencia según los tipos en los que hay muchas cosas dinámicas que se usan.


sería agradable si usted podría profundizar múltiples niveles a la vez.

var e = new ExpandoObject(); 
e.position.x = 5; 
etc... 

Ese no es el mejor ejemplo posible, imagine usos elegantes según corresponda en sus propios proyectos.

Es una lástima que no puedas tener código para construir algunos de estos y llevar los resultados a intellisense. Aunque no estoy seguro de cómo funcionaría esto.

Se bueno si pudieran tener un valor tan bueno como los miembros.

var fifteen = new ExpandoObject(); 
fifteen = 15; 
fifteen.tens = 1; 
fifteen.units = 5; 
fifteen.ToString() = "fifteen"; 
etc... 
-1

Después de las ValueTuples, ¿para qué sirve la clase ExpandoObject? este código 6 líneas con ExpandoObject:

dynamic T = new ExpandoObject(); 
T.x = 1; 
T.y = 2; 
T.z = new ExpandoObject(); 
T.z.a = 3; 
T.b= 4; 

se puede escribir en una línea con tuplas:

var T = (x: 1, y: 2, z: (a: 3, b: 4)); 

además con la sintaxis tupla que tiene una fuerte inferencia de tipos y apoyo intlisense

+0

Sus ejemplos no son idénticos en el sentido de que con tupla de valor no se puede escribir T.c = 5; después de terminar defina T. Con ExpandoObject puede hacerlo porque es dinámico. Su ejemplo con value tuple es muy idéntico a la declaración de tipo anónimo. E.g: var T2 = nuevo {x = 1, y = 2, z = nuevo {a = 3, b = 4}}; – LxL

+0

¿Por qué tengo que escribir T.c = 5 sin definirlo? ExpandoObject solo es útil cuando se trata de objetos COM que no son defiend en .net. De lo contrario, nunca uso este ExpandoObject, porque está sucio y con errores tanto en tiempo de diseño como en tiempo de ejecución. –

+0

¿Qué tal si primero has asignado z a (a: 3, b: 4) y luego quieres que z tenga una propiedad c adicional? ¿Puedes hacerlo con valor de tupla? – LxL

Cuestiones relacionadas