2010-08-25 12 views

Respuesta

122

La palabra clave dynamic se utiliza para declarar las variables que se deben enlazar con retraso.
Si desea utilizar el enlace tardío, para cualquier tipo de letra real o imaginaria, use la palabra clave dynamic y el compilador hace el resto.

Cuando utiliza la palabra clave dynamic para interactuar con una instancia normal, el DLR realiza llamadas con destino tardío a los métodos normales de la instancia.

El IDynamicMetaObjectProvider interface permite que una clase controle su comportamiento de límite de tiempo.
Cuando utiliza la palabra clave dynamic para interactuar con una implementación IDynamicMetaObjectProvider, el DLR llama a los métodos IDynamicMetaObjectProvider y el objeto en sí decide qué hacer.

Las clases ExpandoObject y DynamicObject son implementaciones de IDynamicMetaObjectProvider.

ExpandoObject es una clase simple que le permite agregar miembros a una instancia y usarlos al dynamic aliado.
DynamicObject es una implementación más avanzada que se puede heredar para proporcionar fácilmente un comportamiento personalizado.

+2

¿Cuál sería un buen lugar para aprender más sobre esto? ¿No es la API sino el porqué de la API? p.ej. ¿Por qué ExpandoObject no se deriva de DynamicObject, que se ve como el tipo de base de hecho para la programación basada en "method_missing" de ruby. – Gishu

+3

¿Podría agregar algunos ejemplos de uso donde sea posible? Por ejemplo, ¿cómo usaría un DynamicObject y cuáles son los beneficios? –

+6

Las respuestas excelentes sin ejemplos como este son como pastel sin crema en la parte superior. –

32

De acuerdo con la especificación de lenguaje C# dynamic es una declaración de tipo. Es decir. dynamic x significa que la variable x tiene el tipo dynamic.

es un tipo que facilita la implementación de IDynamicMetaObjectProvider y anula el comportamiento de enlace específico para el tipo.

ExpandoObject es un tipo que funciona como un bolso de la propiedad. Es decir. puede agregar propiedades, métodos, etc. a instancias dinámicas de este tipo en tiempo de ejecución.

+17

'dynamic' no es un tipo real ... es sólo una pista para indicar al compilador a utilizar el enlace para esta variable. Las variables 'dynamic' se declaran realmente como' object' en MSIL –

+0

@Thomas: desde el punto de vista del compilador es un tipo, pero tiene razón en que la representación en tiempo de ejecución es la de Object. Encontrará la frase "estáticamente estáticamente tipado" en varias presentaciones de MS. –

+2

@Thomas: y la especificación de idioma dice "C# 4.0 introduce un nuevo tipo estático llamado dinámico". –

20

Intentaré proporcionar una respuesta más clara a esta pregunta, para explicar claramente cuáles son las diferencias entre dynamic, ExpandoObject y DynamicObject.

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

ExpandoObject y DynamicObject son de hecho tipos. En la SUPERFICIE, se ven muy similares entre sí. Ambas clases implementan IDynamicMetaObjectProvider. Sin embargo, ahonda más y verás que NO son similares en absoluto.

DynamicObject es una implementación parcial de IDynamicMetaObjectProvider puramente destinado a ser un punto de partida para los desarrolladores implementar sus propios tipos de soporte envío dinámico con el almacenamiento a medida subyacente y el comportamiento de recuperación para hacer el trabajo de despacho dinámico.

  1. DynamicObject no se puede construir directamente.
  2. DEBE ampliar DynamicObject para que le sea de utilidad como desarrollador.
  3. Al extender DynamicObject, ahora puede proporcionar un comportamiento CUSTOM sobre cómo desea que el despacho dinámico se resuelva en 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 desee. (por ejemplo, cómo obtienes y configuras los datos en el envío depende totalmente de ti).

En resumen, utilice DynamicObject cuando desee crear sus propios tipos que se puedan usar con el DLR y que funcionen con los comportamientos CUSTOM que desee.

Ejemplo: Imagine que desea tener un tipo dinámico que devuelve 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 contenedor". Si desea un objeto dinámico que se comporte así, tendrá que controlar lo que sucede cuando no se encuentra un campo. ExpandoObject no te dejará hacer esto. Por lo tanto, deberá crear su propio tipo con un comportamiento único de resolución dinámica de miembros (despacho) y usarlo en lugar del ExpandoObject listo para usar.

Se puede crear un tipo de la siguiente manera: (. Tenga en cuenta, el código de abajo es sólo para ilustración y no se ejecute Para obtener información sobre cómo utilizar correctamente DynamicObject, 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!") 

ExpandoObject es una implementación completa de IDynamicMetaObjectProvider, donde el equipo de .NET Framework ha hecho 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 (90% del tiempo, ExpandoObject es lo suficientemente bueno). Por ejemplo, consulte lo siguiente, y 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) { ... } 

Entonces, para resumir, ExpandoObject es simplemente una forma de pre-elegido para extender DynamicObject con ciertos comportamientos dinámicos de despacho que probablemente van a trabajar para usted, pero tal vez no en función de sus necesidades particulares.

Considerando que, DyanmicObject es un BaseType auxiliar que hace que la implementación de sus propios tipos con comportamientos dinámicos únicos sea simple y fácil.

A useful tutorial on which much of the example source above is based.

0

El ejemplo anterior de DynamicObject no le dice a la diferencia con claridad, porque es básicamente la implementación de la funcionalidad que ya está proporcionada por ExpandoObject.

En los dos enlaces se mencionan a continuación, es muy claro que con la ayuda de DynamicObject, es posible preservar/cambiar el tipo real (XElement en el ejemplo utilizado en enlaces más abajo) y un mejor control sobre las propiedades y 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; 

     } 

    } 

}