Différences entre ExpandoObject, DynamicObject et dynamic

170

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

Dans quelles situations utilisez-vous ces types?

M4N
la source

Réponses:

154

Le dynamicmot-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 dynamicmot - clé pour interagir avec une instance normale, le DLR effectue des appels à liaison tardive aux méthodes normales de l'instance.

L' IDynamicMetaObjectProviderinterface permet à une classe de prendre le contrôle de son comportement lié tardivement.
Lorsque vous utilisez le dynamicmot clé pour interagir avec une IDynamicMetaObjectProviderimplémentation, le DLR appelle les IDynamicMetaObjectProviderméthodes et l'objet lui-même décide quoi faire.

Les classes ExpandoObjectet DynamicObjectsont des implémentations de IDynamicMetaObjectProvider.

ExpandoObjectest une classe simple qui vous permet d'ajouter des membres à une instance et de les utiliser dynamicalliés.
DynamicObjectest une implémentation plus avancée qui peut être héritée pour fournir facilement un comportement personnalisé.

SLaks
la source
2
Quel serait le bon endroit pour en savoir plus à ce sujet? Pas l'API mais le pourquoi de l'API? Par exemple, pourquoi ExpandoObject ne dérive-t-il pas de DynamicObject, qui recherche le type de base de facto pour la programmation basée sur «method_missing» de ruby.
Gishu le
4
Pourriez-vous ajouter des exemples d'utilisation lorsque cela est possible? Par exemple, comment utiliser un DynamicObject et quels sont les avantages?
oɔɯǝɹ
10
Les bonnes réponses sans exemples comme celui-ci sont comme un gâteau sans crème sur le dessus.
Teoman shipahi
68

Je vais essayer de fournir une réponse plus claire à cette question, d'expliquer clairement quelles sont les différences entre dynamique ExpandoObjectet DynamicObject.

Très vite, dynamicc'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 dynamicle reste de cette réponse.

ExpandoObjectet DynamicObjectsont 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 IDynamicMetaObjectProviderpurement 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.

  1. DynamicObject ne peut pas être construit directement.
  2. Vous DEVEZ étendre DynamicObject pour qu'il vous soit utile en tant que développeur.
  3. Lorsque vous étendez DynamicObject, vous êtes désormais en mesure de fournir un comportement CUSTOM concernant la façon dont vous souhaitez que la répartition dynamique se résout 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 les données où et comme vous le souhaitez. (par exemple, la façon dont vous obtenez et définissez les données lors de l'expédition dépend entièrement de vous).

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!")

ExpandoObjectest 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, ExpandoObjectc'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, ExpandoObjectest 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, DyanmicObjectest 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é.

Ayo je
la source
Très bonne explication. Juste une correction technique: ExpandoObject n'hérite pas de DynamicObject.
Mike Rosoft
Une petite correction sur l'exemple pour DynamicObject: lors de la substitution TryGetMember, si vous retournez false, un RuntimeBinderExceptionsera renvoyé lors de la tentative d'accès à une propriété non existante. Pour que l'extrait fonctionne réellement, vous devez revenir true.
lluchmk
36

Selon la spécification du langage C #, il dynamicy a une déclaration de type. Ie dynamic xsignifie que la variable xa le type dynamic.

DynamicObjectest un type qui facilite l'implémentation IDynamicMetaObjectProvideret remplace ainsi le comportement de liaison spécifique du type.

ExpandoObjectest 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.

Brian Rasmussen
la source
25
dynamicn'est pas un type réel ... c'est juste un indice pour dire au compilateur d'utiliser une liaison tardive pour cette variable. dynamicles variables sont effectivement déclarées comme objectdans MSIL
Thomas Levesque
1
@Thomas: du point de vue du compilateur, c'est un type, mais vous avez raison de dire que la représentation d'exécution est celle d'Object. Vous trouverez la déclaration «statiquement typée pour être dynamique» dans plusieurs présentations MS.
Brian Rasmussen
3
@Thomas: et la spécification du langage dit "C # 4.0 introduit un nouveau type statique appelé dynamique".
Brian Rasmussen
en effet ... Mais je pense que c'est déroutant de le considérer comme un type, car il n'y a pas de relation d'héritage avec des types comme DynamicObject ou ExpandoObject
Thomas Levesque
3
@NathanA Je suis avec vous ici. Cependant, la spécification du langage l'appelle un type, c'est donc ce que je veux dire.
Brian Rasmussen
0

L'exemple ci-dessus de DynamicObjectne 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 ( XElementdans 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;    
        }    
    }    
}
Deepak Mishra
la source