¿Cuáles son los verdaderos beneficios de ExpandoObject?


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

¿Hay alguna ventaja en esto sobre el uso de un Dictionary<string,object>, o realmente incluso un 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"]);

¿Qué real las ventajas se obtienen al usar ExpandoObject en lugar de simplemente usar un tipo de diccionario arbitrario, aparte de no ser obvio que está utilizando un tipo que se va a determinar en tiempo de ejecución.

Author: Reed Copsey, 2009-10-31

10 answers

Ya que escribí el artículo de MSDN al que se refiere, supongo que tengo que responder a este.

Primero, anticipé esta pregunta y es por eso que escribí una entrada de blog que muestra un caso de uso más o menos real para ExpandoObject: Dynamic en C# 4.0: Introduciendo el ExpandoObject.

En breve, ExpandoObject puede ayudarle 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"]);

Lo más profundo 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 ha señalado, ExpandoObject implementa la interfaz INotifyPropertyChanged que le da más control sobre las propiedades que un diccionario.

Finalmente, puedes 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);
   }
}
 625
Author: Alexandra Rusina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-12 19:28:41

Una ventaja es para los escenarios de enlace. Las cuadrículas de datos y las cuadrículas 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 enlazar a un objeto ExpandoObject más fácilmente que a un diccionario.

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

 70
Author: itowlson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-31 01:17:31

El verdadero beneficio para mí es el enlace de datos totalmente sin esfuerzo de XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

 <TextBlock Text="{Binding SomeData.WhatEver}" />
 38
Author: bjull,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-01 11:51:40

El interop con otros lenguajes basados en DLR es la razón #1 que se me ocurre. No puedes pasarles un Dictionary<string, object> ya que no es un IDynamicMetaObjectProvider. Otro beneficio adicional es que implementa INotifyPropertyChanged lo que significa que en el mundo de databinding de WPF también tiene beneficios adicionales más allá de lo que Dictionary<K,V> puede proporcionarle.

 26
Author: Drew Marsh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-23 19:25:45

Se trata de la conveniencia del programador. Puedo imaginar escribir programas rápidos y sucios con este objeto.

 18
Author: ChaosPandion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-31 01:15:46

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

Eso, e interop con lenguajes dinámicos, creo.

 13
Author: gn22,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-31 01:19:57

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

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

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 cierta lógica en el objeto dinámico en tiempo de ejecución. Por lo tanto, junto con las expresiones lambda, closures, dynamic keyword y DynamicObject class, podemos introducir algunos elementos de programación en nuestro código C#, que conocemos de lenguajes dinámicos como JavaScript o PHP.

 9
Author: sgnsajgon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-11 16:17:33

Hay algunos casos en los que esto es útil. Lo usaré para un shell modularizado, por ejemplo. Cada módulo define su propio Diálogo de configuración asociado a su configuración. Lo proveo con un ExpandoObject ya que es Datacontext y guardo los valores en mi almacenamiento de configuración. De esta manera, el escritor de diálogo de configuración solo tiene que enlazar a un Valor y se crea y guarda automáticamente. (Y proporcionado al módulo para el uso de estos ajustes, por supuesto)

Es simplemente más fácil de usar que diccionario. Pero todo el mundo debe ser consciente de que internamente es solo un diccionario.

Es como LINQ solo azúcar sintáctico, 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<string,object> (Incluso se puede convertir en uno para enumerar los valores).

 4
Author: n1LWeb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-16 12:17:49
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);

Creo que solo funciona porque todo tiene un toString(), de lo contrario tendría que saber el tipo que era y lanzar el 'objeto' a ese tipo.


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

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

  2. Parece como si esto pudiera ser utilizado como una Tupla muy agradable. Puedes todavía llame a sus miembros "Item1", "Item2", etc... pero ahora no tienes que hacerlo, también es mutable, a diferencia de una Tupla. Esto tiene el enorme 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 conducir a convenciones de nombres que se codifican, y tratar de trabajar con morfemas y sílabas cuando el código está tratando de entender cómo usar miembros - P

  4. ¿Puedes asignar un valor a un ExpandoObject en sí o solo a sus miembros? Compare y contrasta 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 puedes usar ExpandoObject.

  6. No se puede usar dynamic como un miembro de datos en una clase, tal vez porque es al menos una especie de palabra clave, con suerte se puede con Ampliar objeto.

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


Sería bueno si pudieras profundizar en 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 como apropiados en sus propios proyectos.

Es una pena que no pueda tener código construir algunos de estos y empujar el resultados a intellisense. No estoy seguro de cómo funcionaría esto.

Sería bueno si pudieran tener un valor, así como los miembros.

var fifteen = new ExpandoObject();
fifteen = 15;
fifteen.tens = 1;
fifteen.units = 5;
fifteen.ToString() = "fifteen";
etc...
 0
Author: alan2here,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-05 18:14:31

Después de valueTuples, ¿cuál es el uso de la clase ExpandoObject? este código de 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 de la sintaxis de tupla, tiene una fuerte inferencia de tipos y soporte de intlisense

 -1
Author: Eng. M.Hamdy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-22 00:21:28