web-dev-qa-db-fra.com

Différences entre ExpandoObject, DynamicObject et dynamic

Quelles sont les différences entre System.Dynamic.ExpandoObject, System.Dynamic.DynamicObject et dynamic?

Dans quelles situations utilisez-vous ces types?

151
M4N

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 dynamically.
DynamicObject est une implémentation plus avancée qui peut être héritée pour fournir facilement un comportement personnalisé.

136
SLaks

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.

  1. DynamicObject ne peut pas être construit directement.
  2. Vous DEVEZ étendre DynamicObject pour qu'il puisse vous être utile en tant que développeur.
  3. Lorsque vous étendez DynamicObject, vous pouvez désormais fournir un comportement CUSTOM concernant la résolution de la répartition dynamique en données stockées en interne dans votre représentation de données sous-jacente au moment de l'exécution.
  4. ExpandoObject stocke les données sous-jacentes dans un dictionnaire, etc. Si vous implémentez DynamicObject, vous pouvez stocker des données où et comme vous le souhaitez. (par exemple, la manière dont vous obtenez et définissez les données lors de l’envoi dépend de vous).

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.

39
Ayo I

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. 

34
Brian Rasmussen

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.

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;    
        }    
    }    
}
0
Deepak Mishra