Quelles sont les différences entre System.Dynamic.ExpandoObject
, System.Dynamic.DynamicObject
et dynamic
?
Dans quelles situations utilisez-vous ces types?
Le mot clé dynamic
est utilisé pour déclarer des variables qui doivent être liées tardivement.
Si vous souhaitez utiliser une liaison tardive, pour tout type réel ou imaginaire, utilisez le mot clé dynamic
et le compilateur fait le reste.
Lorsque vous utilisez le mot clé dynamic
pour interagir avec une instance normale, le DLR effectue des appels liés tardivement aux méthodes normales de l'instance.
L'interface IDynamicMetaObjectProvider
permet à une classe de prendre le contrôle de son comportement lié tardivement.
Lorsque vous utilisez le mot clé dynamic
pour interagir avec une implémentation IDynamicMetaObjectProvider
, le DLR appelle les méthodes IDynamicMetaObjectProvider
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
ally.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, en expliquant clairement quelles sont les différences entre dynamique, ExpandoObject
et DynamicObject
.
Très rapidement, dynamic
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 d'utiliser plutôt une liaison tardive au moment de l'exécution. Nous n'allons donc pas consacrer beaucoup de temps à dynamic
dans 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 et vous constaterez qu'ils ne sont PAS du tout similaires.
DynamicObject est une implémentation partielle de IDynamicMetaObjectProvider
dans le seul but de permettre aux développeurs d'implémenter leurs propres types personnalisés prenant en charge la répartition dynamique avec un comportement de stockage et de récupération sous-jacent personnalisé pour que la répartition dynamique fonctionne.
En bref, utilisez DynamicObject lorsque vous souhaitez créer vos types OWN pouvant être utilisés avec le DLR et travailler avec les comportements CUSTOM de votre choix.
Exemple: Imaginez que vous souhaitiez avoir un type dynamique qui renvoie une valeur par défaut personnalisée chaque fois qu'une tentative d'obtention sur un membre n'existe PAS (c'est-à-dire qu'elle n'a pas été ajoutée au moment de l'exécution). Et ce 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 de la sorte, vous devez contrôler ce qui se passe lorsqu'un champ est introuvable. ExpandoObject ne vous laissera pas faire cela. Il vous faudra donc créer votre propre type avec un comportement unique de résolution de membres dynamiques (dispatch) et l'utiliser à la place de la ExpandoObject
prête à l'emploi__.
Vous pouvez créer un type de la manière suivante: (Notez que le code ci-dessous est juste pour illustration et ne peut pas s'exécuter. Pour apprendre à utiliser correctement DynamicObject, vous trouverez de nombreux articles et tutoriels 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;
}
}
Nous pourrions maintenant utiliser cette classe imaginaire que nous venons de créer comme type dynamique ayant 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 FULL 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 estimez que ExpandoObject fonctionne assez bien pour vous (90% du temps, ExpandoObject
est assez bon). Ainsi, par exemple, voir 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édéfini d’étendre DynamicObject avec certains comportements de dispatch dynamiques qui fonctionneront probablement pour vous, mais qui ne dépendent peut-être pas de vos besoins particuliers.
Alors que DyanmicObject
est un type de base d'assistance qui facilite et simplifie l'implémentation de vos propres types avec des comportements dynamiques uniques.
Un tutoriel utile sur lequel sont basés la plupart des exemples de sources ci-dessus.
Selon la spécification du langage C #, dynamic
est une déclaration de type. C'est à dire. dynamic x
signifie que la variable x
a le type dynamic
.
DynamicObject
est un type qui facilite l'implémentation de 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. vous pouvez ajouter des propriétés, des méthodes, etc., à des instances dynamiques de ce type lors de l'exécution.
L'exemple ci-dessus de DynamicObject
ne dit pas clairement la différence, car il implémente essentiellement la fonctionnalité déjà fournie par ExpandoObject
.
Dans les deux liens mentionnés ci-dessous, il est très clair qu'avec DynamicObject
, il est possible de conserver/modifier le type actuel (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;
}
}
}