Réponses:
Le dynamic
mot-clé est utilisé pour déclarer des variables qui doivent être liées tardivement.
Si vous souhaitez utiliser la liaison tardive, pour tout type réel ou imaginaire, vous utilisez ledynamic
mot clé et le compilateur s'occupe du reste.
Lorsque vous utilisez le dynamic
mot - clé pour interagir avec une instance normale, le DLR effectue des appels à liaison tardive aux méthodes normales de l'instance.
L' IDynamicMetaObjectProvider
interface permet à une classe de prendre le contrôle de son comportement lié tardivement.
Lorsque vous utilisez le dynamic
mot clé pour interagir avec une IDynamicMetaObjectProvider
implémentation, le DLR appelle les IDynamicMetaObjectProvider
méthodes et l'objet lui-même décide quoi faire.
Les classes ExpandoObject
et DynamicObject
sont des implémentations de IDynamicMetaObjectProvider
.
ExpandoObject
est une classe simple qui vous permet d'ajouter des membres à une instance et de les utiliser dynamic
alliés.
DynamicObject
est une implémentation plus avancée qui peut être héritée pour fournir facilement un comportement personnalisé.
Je vais essayer de fournir une réponse plus claire à cette question, d'expliquer clairement quelles sont les différences entre dynamique ExpandoObject
et DynamicObject
.
Très vite, dynamic
c'est un mot-clé. Ce n'est pas un type en soi. C'est un mot-clé qui indique au compilateur d'ignorer la vérification de type statique au moment du design et à la place d'utiliser la liaison tardive au moment de l'exécution. Nous n'allons donc pas passer beaucoup de temps sur dynamic
le reste de cette réponse.
ExpandoObject
et DynamicObject
sont en effet des types. Sur la SURFACE, ils se ressemblent beaucoup. Les deux classes implémentent IDynamicMetaObjectProvider
. Cependant, creusez plus profondément et vous constaterez qu'ils ne sont PAS du tout similaires.
DynamicObject est une implémentation partielle de IDynamicMetaObjectProvider
purement destinée à être un point de départ pour que les développeurs implémentent leurs propres types personnalisés prenant en charge la répartition dynamique avec un stockage sous-jacent personnalisé et un comportement de récupération pour faire fonctionner la répartition dynamique.
En bref, utilisez DynamicObject lorsque vous souhaitez créer vos propres types qui peuvent être utilisés avec le DLR et travailler avec les comportements CUSTOM que vous souhaitez.
Exemple: imaginez que vous aimeriez avoir un type dynamique qui renvoie une valeur par défaut personnalisée chaque fois qu'un get est tenté sur un membre qui n'existe PAS (c'est-à-dire qui n'a pas été ajouté au moment de l'exécution). Et cette valeur par défaut dira: "Je suis désolé, il n'y a pas de cookies dans ce pot!". Si vous voulez un objet dynamique qui se comporte comme ça, vous devrez contrôler ce qui se passe lorsqu'un champ n'est pas trouvé. ExpandoObject ne vous permettra pas de faire cela. Vous devrez donc créer votre propre type avec un comportement de résolution de membre dynamique unique (répartition) et l'utiliser à la place du tout prêtExpandoObject
.
Vous pouvez créer un type comme suit: (Notez que le code ci-dessous est juste à titre d'illustration et ne peut pas s'exécuter. Pour savoir comment utiliser correctement DynamicObject, il existe de nombreux articles et didacticiels ailleurs.)
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;
}
}
Maintenant, nous pourrions utiliser cette classe imaginaire que nous venons de créer comme type dynamique qui a un comportement très personnalisé si le champ n'existe pas.
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
est une implémentation COMPLÈTE de IDynamicMetaObjectProvider
, où l'équipe .NET Framework a pris toutes ces décisions pour vous. Ceci est utile si vous n'avez besoin d'aucun comportement personnalisé et que vous pensez qu'ExpandoObject fonctionne assez bien pour vous (90% du temps, ExpandoObject
c'est assez bien). Ainsi, par exemple, voyez ce qui suit, et que pour ExpandoObject, les concepteurs ont choisi de lever une exception si le membre dynamique n'existe pas.
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) { ... }
Donc, pour résumer, ExpandoObject
est simplement un moyen pré-choisi d'étendre DynamicObject avec certains comportements de répartition dynamique qui fonctionneront probablement pour vous , mais peuvent ne pas dépendre de vos besoins particuliers.
Considérant que, DyanmicObject
est une aide BaseType qui facilite et simplifie l'implémentation de vos propres types avec des comportements dynamiques uniques.
Un didacticiel utile sur lequel une grande partie de l'exemple de source ci-dessus est basé.
DynamicObject
: lors de la substitution TryGetMember
, si vous retournez false, un RuntimeBinderException
sera renvoyé lors de la tentative d'accès à une propriété non existante. Pour que l'extrait fonctionne réellement, vous devez revenir true
.
Selon la spécification du langage C #, il dynamic
y a une déclaration de type. Ie dynamic x
signifie que la variable x
a le type dynamic
.
DynamicObject
est un type qui facilite l'implémentation IDynamicMetaObjectProvider
et remplace ainsi le comportement de liaison spécifique du type.
ExpandoObject
est un type qui agit comme un sac de propriété. C'est-à-dire que vous pouvez ajouter des propriétés, des méthodes, etc. aux instances dynamiques de ce type lors de l'exécution.
dynamic
n'est pas un type réel ... c'est juste un indice pour dire au compilateur d'utiliser une liaison tardive pour cette variable. dynamic
les variables sont effectivement déclarées comme object
dans MSIL
L'exemple ci-dessus de DynamicObject
ne fait pas clairement la différence, car il implémente essentiellement la fonctionnalité déjà fournie parExpandoObject
.
Dans les deux liens mentionnés ci-dessous, il est très clair qu'avec l'aide de DynamicObject
, il est possible de conserver / modifier le type réel ( XElement
dans l'exemple utilisé dans les liens ci-dessous) et de mieux contrôler les propriétés et les méthodes.
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;
}
}
}