¿Cuáles son los verdaderos beneficios de ExpandoObject?

La clase ExpandoObject que se agrega a .NET 4 le permite establecer arbitrariamente propiedades en un objeto en tiempo de ejecución.

¿Hay alguna ventaja en esto sobre el uso de un Dictionary , o incluso una Hashtable ? Por lo que puedo decir, esto no es más que una tabla hash a la que se puede acceder con una syntax 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(); obj["MyInt"] = 3; obj["MyString"] = "Foo"; Console.WriteLine(obj["MyString"]); 

Qué ventajas reales se obtienen al usar ExpandoObject en lugar de simplemente usar un tipo de diccionario arbitrario, aparte de no ser obvio que está usando un tipo que se determinará en tiempo de ejecución.

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: Presentación del ExpandoObject .

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

 Dictionary dict = new Dictionary(); Dictionary address = new Dictionary(); dict["Address"] = address; address["State"] = "WA"; Console.WriteLine(((Dictionary)dict["Address"])["State"]); 

Cuanto más profunda es la jerarquía, 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 brinda más control sobre las propiedades que un diccionario.

Finalmente, puede agregar 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); } } 

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 dynamics, que esperan propiedades DLR en lugar de entradas de diccionario, también puede ser una consideración en algunos escenarios.

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

 public dynamic SomeData { get; set; } 

 SomeData.WhatEver = "Yo Man!"; 

   

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

Se trata de la conveniencia del progtwigdor. Me puedo imaginar escribiendo progtwigs rápidos y sucios con este objeto.

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

Eso, y la interoperabilidad con lenguajes dynamics, pensaría.

Es un ejemplo del gran artículo de MSDN sobre el uso de ExpandoObject para crear tipos ad-hoc dynamics para los datos estructurados entrantes (es decir, XML, Json).

También podemos asignar un delegado a la propiedad dinámica de ExpandoObject :

 dynamic person = new ExpandoObject(); person.FirstName = "Dino"; person.LastName = "Esposito"; person.GetFullName = (Func)(() => { return String.Format("{0}, {1}", person.LastName, person.FirstName); }); var name = person.GetFullName(); Console.WriteLine(name); 

Por lo tanto, nos permite inyectar un poco de lógica en un objeto dynamic en tiempo de ejecución. Por lo tanto, junto con las expresiones lambda, los cierres, la palabra clave dinámica y la clase DynamicObject , podemos introducir algunos elementos de progtwigción funcional en nuestro código C #, que conocemos desde lenguajes dynamics como JavaScript o PHP.

Hay algunos casos donde 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 proporcionado 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.

Así que 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 (Incluso puede convertirlo en uno para enumerar los valores).

 var obj = new Dictionary; ... Console.WriteLine(obj["MyString"]); 

Creo que solo funciona porque todo tiene un ToString (); de lo contrario, tendrías que saber el tipo que era y convertir el ‘objeto’ en 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 acceder a una colección, en este caso lo que efectivamente es un “diccionario”, usando la notación de 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”, al igual que la sensación con el diccionario, puede sentir que es demasiado como “ejecutar cadenas”, y puede dar lugar a que se codifiquen las convenciones de nombres y al trabajar con morfemas y Sílabas cuando el código intenta entender cómo usar miembros 😛

  4. ¿Puedes asignarle un valor a un ExpandoObject o solo a sus miembros? Compare y contraste con dynamic / dynamic [], 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 Dynamic como un miembro de datos en una clase, tal vez porque es al menos una especie de palabra clave, con la esperanza de que pueda hacerlo con ExpandoObject.

  7. Supongo 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 se usan muchas cosas dinámicas.


Sé agradable si puedes profundizar en varios 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 pena 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.

Sean agradables 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... 

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

 dynamic T = new ExpandoObject(); Tx = 1; Ty = 2; Tz = new ExpandoObject(); Tza = 3; Tb= 4; 

puede escribirse en una línea con tuplas:

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

además de la syntax de tupla, tiene una inferencia de tipo fuerte y soporte intlisense