Diferencias entre ExpandoObject, DynamicObject y dynamic

Respuestas:

154

La dynamicpalabra clave se utiliza para declarar variables que deberían estar vinculadas tarde.
Si desea usar el enlace tardío, para cualquier tipo real o imaginado, use la dynamicpalabra clave y el compilador hace el resto.

Cuando utiliza la dynamicpalabra clave para interactuar con una instancia normal, el DLR realiza llamadas con retraso a los métodos normales de la instancia.

La IDynamicMetaObjectProviderinterfaz permite que una clase tome el control de su comportamiento de enlace tardío.
Cuando utiliza la dynamicpalabra clave para interactuar con una IDynamicMetaObjectProviderimplementación, el DLR llama a los IDynamicMetaObjectProvidermétodos y el propio objeto decide qué hacer.

Las clases ExpandoObjecty DynamicObjectson implementaciones de IDynamicMetaObjectProvider.

ExpandoObjectes una clase simple que le permite agregar miembros a una instancia y usarlos dynamicaliados.
DynamicObjectes una implementación más avanzada que se puede heredar para proporcionar fácilmente un comportamiento personalizado.

SLaks
fuente
2
¿Cuál sería un buen lugar para aprender más sobre esto? ¿No es la API sino el por qué detrás de la API? Por ejemplo, ¿por qué ExpandoObject no se deriva de DynamicObject, que busca el tipo de base de facto para la programación basada en 'método_mision' de ruby?
Gishu
44
¿Podría agregar algunos ejemplos de uso cuando sea posible? Por ejemplo, ¿cómo usaría un DynamicObject y cuáles son los beneficios?
2014
10
Grandes respuestas sin ejemplos como este son como pastel sin crema encima.
Teoman shipahi
68

Trataré de proporcionar una respuesta más clara a esta pregunta, para explicar claramente cuáles son las diferencias entre dinámico ExpandoObjecty DynamicObject.

Muy rápido, dynamices una palabra clave. No es un tipo per-se. Es una palabra clave que le dice al compilador que ignore la verificación de tipo estático en tiempo de diseño y que, en su lugar, use el enlace tardío en tiempo de ejecución. Así que no vamos a pasar mucho tiempo dynamicen el resto de esta respuesta.

ExpandoObjecty de DynamicObjecthecho son tipos. En la SUPERFICIE, se ven muy similares entre sí. Ambas clases implementan IDynamicMetaObjectProvider. Sin embargo, profundice y encontrará que NO son similares en absoluto.

DynamicObject es una implementación parcial que IDynamicMetaObjectProviderpretende ser un punto de partida para que los desarrolladores implementen sus propios tipos personalizados que admiten el despacho dinámico con un comportamiento de recuperación y almacenamiento subyacente personalizado para que el despacho dinámico funcione.

  1. DynamicObject no se puede construir directamente.
  2. DEBE extender DynamicObject para que le sirva de desarrollador.
  3. Cuando extiende DynamicObject, ahora puede proporcionar un comportamiento PERSONALIZADO con respecto a cómo desea que el despacho dinámico se resuelva en los datos almacenados internamente en su representación de datos subyacente en tiempo de ejecución.
  4. ExpandoObject almacena datos subyacentes en un diccionario, etc. Si implementa DynamicObject, puede almacenar datos donde y como quiera. (por ejemplo, cómo obtiene y establece los datos en el envío depende totalmente de usted).

En resumen, use DynamicObject cuando quiera crear sus PROPIOS tipos que puedan usarse con el DLR y trabajar con cualquier comportamiento PERSONALIZADO que desee.

Ejemplo: imagine que le gustaría tener un tipo dinámico que devuelva un valor predeterminado personalizado cada vez que se intenta obtener un miembro que NO existe (es decir, no se ha agregado en tiempo de ejecución). Y ese valor predeterminado dirá: "Lo siento, no hay cookies en este frasco". Si desea un objeto dinámico que se comporte así, deberá controlar lo que sucede cuando no se encuentra un campo. ExpandoObject no te permitirá hacer esto. Por lo tanto, deberá crear su propio tipo con un comportamiento único de resolución dinámica de miembro (despacho) y usarlo en lugar del listo ExpandoObject.

Puede crear un tipo de la siguiente manera: (Tenga en cuenta que el siguiente código es solo para ilustración y puede que no se ejecute. Para obtener información sobre cómo usar DynamicObject correctamente, hay muchos artículos y tutoriales en otros lugares).

public class MyNoCookiesInTheJarDynamicObject : DynamicObject
{
    Dictionary<string, object> properties = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (properties.ContainsKey(binder.Name))
        {
            result = properties[binder.Name];
            return true;
        }
        else
        {
            result = "I'm sorry, there are no cookies in this jar!"; //<-- THIS IS OUR 
            CUSTOM "NO COOKIES IN THE JAR" RESPONSE FROM OUR DYNAMIC TYPE WHEN AN UNKNOWN FIELD IS ACCESSED
            return false;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        properties[binder.Name] = value;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        dynamic method = properties[binder.Name];
        result = method(args[0].ToString(), args[1].ToString());
        return true;
    }
}

Ahora, podríamos usar esta clase imaginaria que acabamos de crear como un tipo dinámico que tiene un comportamiento muy personalizado si el campo no existe.

dynamic d = new MyNoCookiesInTheJarDynamicObject();
var s = d.FieldThatDoesntExist;

//in our contrived example, the below should evaluate to true
Assert.IsTrue(s == "I'm sorry, there are no cookies in this jar!")

ExpandoObjectes una implementación COMPLETA de IDynamicMetaObjectProvider, donde el equipo de .NET Framework ha tomado todas estas decisiones por usted. Esto es útil si no necesita ningún comportamiento personalizado y siente que ExpandoObject funciona lo suficientemente bien para usted (el 90% del tiempo, ExpandoObjectes lo suficientemente bueno). Entonces, por ejemplo, vea lo siguiente, y que para ExpandoObject, los diseñadores optaron por lanzar una excepción si el miembro dinámico no existe.

dynamic d = new ExpandoObject();

/*
The ExpandoObject designers chose that this operation should result in an 
Exception. They did not have to make that choice, null could 
have been returned, for example; or the designers could've returned a "sorry no cookies in the jar" response like in our custom class. However, if you choose to use 
ExpandoObject, you have chosen to go with their particular implementation 
of DynamicObject behavior.
*/

try {
var s = d.FieldThatDoesntExist;
}
catch(RuntimeBinderException) { ... }

En resumen, ExpandoObjectes simplemente una forma preseleccionada de extender DynamicObject con ciertos comportamientos de despacho dinámico que probablemente funcionarán para usted , pero que pueden no depender de sus necesidades particulares.

Mientras que, DyanmicObjectes un BaseType auxiliar que hace que implementar sus propios tipos con comportamientos dinámicos únicos sea simple y fácil.

Un tutorial útil en el que se basa gran parte de la fuente de ejemplo anterior.

Ayo I
fuente
Muy buena explicación. Solo una corrección técnica: ExpandoObject no hereda de DynamicObject.
Mike Rosoft
Una pequeña corrección en el ejemplo para DynamicObject: al anular TryGetMember, si devuelve falso RuntimeBinderException, se lanzará un al intentar acceder a propiedades no existentes. Para que el fragmento realmente funcione, debes regresar true.
lluchmk
36

De acuerdo con la especificación del lenguaje C # dynamices una declaración de tipo. Es decir dynamic x, la variable xtiene el tipo dynamic.

DynamicObjectes un tipo que facilita su implementación IDynamicMetaObjectProvidery, por lo tanto, anula el comportamiento de enlace específico para el tipo.

ExpandoObjectes un tipo que actúa como una bolsa de propiedades. Es decir, puede agregar propiedades, métodos, etc. a instancias dinámicas de este tipo en tiempo de ejecución.

Brian Rasmussen
fuente
25
dynamicno es un tipo real ... es solo una pista para decirle al compilador que use el enlace tardío para esta variable. dynamiclas variables se declaran realmente como objecten MSIL
Thomas Levesque
1
@Thomas: desde el punto de vista del compilador es un tipo, pero tienes razón en que la representación en tiempo de ejecución es la de Object. Encontrará la declaración "estáticamente escrita para ser dinámica" en varias presentaciones de MS.
Brian Rasmussen
3
@Thomas: y la especificación del lenguaje dice "C # 4.0 introduce un nuevo tipo estático llamado dinámico".
Brian Rasmussen
de hecho ... Pero creo que es confuso considerarlo como un tipo, ya que no existe una relación de herencia con tipos como DynamicObject o ExpandoObject
Thomas Levesque
3
@NathanA Estoy contigo aquí. Sin embargo, la especificación del lenguaje lo llama un tipo, así que eso es con lo que voy.
Brian Rasmussen
0

El ejemplo anterior de DynamicObjectno dice la diferencia claramente, porque básicamente está implementando la funcionalidad que ya proporciona ExpandoObject.

En los dos enlaces mencionados a continuación, es muy claro que, con la ayuda de DynamicObject, es posible preservar / cambiar el tipo real ( XElementen el ejemplo utilizado en los enlaces a continuación) y un mejor control sobre las propiedades y los métodos.

https://blogs.msdn.microsoft.com/csharpfaq/2009/09/30/dynamic-in-c-4-0-introducing-the-expandoobject/

https://blogs.msdn.microsoft.com/csharpfaq/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject/

public class DynamicXMLNode : DynamicObject    
{    
    XElement node;

    public DynamicXMLNode(XElement node)    
    {    
        this.node = node;    
    }

    public DynamicXMLNode()    
    {    
    }

    public DynamicXMLNode(String name)    
    {    
        node = new XElement(name);    
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)    
    {    
        XElement setNode = node.Element(binder.Name);

        if (setNode != null)    
            setNode.SetValue(value);    
        else    
        {    
            if (value.GetType() == typeof(DynamicXMLNode))    
                node.Add(new XElement(binder.Name));    
            else    
                node.Add(new XElement(binder.Name, value));    
        }

        return true;    
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)    
    {    
        XElement getNode = node.Element(binder.Name);

        if (getNode != null)    
        {    
            result = new DynamicXMLNode(getNode);    
            return true;    
        }    
        else    
        {    
            result = null;    
            return false;    
        }    
    }    
}
Deepak Mishra
fuente