Respuestas:
La dynamic
palabra 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 dynamic
palabra clave y el compilador hace el resto.
Cuando utiliza la dynamic
palabra clave para interactuar con una instancia normal, el DLR realiza llamadas con retraso a los métodos normales de la instancia.
La IDynamicMetaObjectProvider
interfaz permite que una clase tome el control de su comportamiento de enlace tardío.
Cuando utiliza la dynamic
palabra clave para interactuar con una IDynamicMetaObjectProvider
implementación, el DLR llama a los IDynamicMetaObjectProvider
métodos y el propio objeto 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 dynamic
aliados.
DynamicObject
es una implementación más avanzada que se puede heredar para proporcionar fácilmente un comportamiento personalizado.
Trataré de proporcionar una respuesta más clara a esta pregunta, para explicar claramente cuáles son las diferencias entre dinámico 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 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 dynamic
en el resto de esta respuesta.
ExpandoObject
y de DynamicObject
hecho 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 IDynamicMetaObjectProvider
pretende 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.
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!")
ExpandoObject
es 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, ExpandoObject
es 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, ExpandoObject
es 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, DyanmicObject
es 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.
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
.
De acuerdo con la especificación del lenguaje C # dynamic
es una declaración de tipo. Es decir dynamic x
, la variable x
tiene el tipo dynamic
.
DynamicObject
es un tipo que facilita su implementación IDynamicMetaObjectProvider
y, por lo tanto, anula el comportamiento de enlace específico para el tipo.
ExpandoObject
es 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.
dynamic
no es un tipo real ... es solo una pista para decirle al compilador que use el enlace tardío para esta variable. dynamic
las variables se declaran realmente como object
en MSIL
El ejemplo anterior de DynamicObject
no 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 ( XElement
en el ejemplo utilizado en los enlaces a continuación) y un mejor control sobre las propiedades y los métodos.
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;
}
}
}