Differenze tra ExpandoObject, DynamicObject e dinamico


170

Quali sono le differenze tra System.Dynamic.ExpandoObject, System.Dynamic.DynamicObjecte dynamic?

In quali situazioni usi questi tipi?

Risposte:


154

La dynamicparola chiave viene utilizzata per dichiarare variabili che devono essere associate in ritardo.
Se si desidera utilizzare l'associazione tardiva, per qualsiasi tipo reale o immaginario, si utilizza la dynamicparola chiave e il compilatore fa il resto.

Quando si utilizza la dynamicparola chiave per interagire con un'istanza normale, il DLR esegue chiamate associate in ritardo ai metodi normali dell'istanza.

L' IDynamicMetaObjectProviderinterfaccia consente a una classe di assumere il controllo del suo comportamento in ritardo.
Quando si utilizza la dynamicparola chiave per interagire con IDynamicMetaObjectProviderun'implementazione, il DLR chiama i IDynamicMetaObjectProvidermetodi e l'oggetto stesso decide cosa fare.

Le classi ExpandoObjecte DynamicObjectsono implementazioni di IDynamicMetaObjectProvider.

ExpandoObjectè una classe semplice che ti consente di aggiungere membri a un'istanza e usarli dynamicalleati.
DynamicObjectè un'implementazione più avanzata che può essere ereditata per fornire facilmente comportamenti personalizzati.


2
Quale sarebbe un buon posto per saperne di più su questo? Non l'API ma il perché dietro l'API? es. Perché ExpandoObject non deriva da DynamicObject, che cerca il tipo di base defacto per la programmazione basata su 'method_missing' di ruby.
Gishu,

4
Potresti aggiungere alcuni esempi di utilizzo ove possibile? Ad esempio, come dovrei usare un DynamicObject e quali sono i vantaggi?
oɔɯǝɹ

10
Grandi risposte senza esempi come questo sono come una torta senza crema in cima.
Teoman Shipahi,


68

Cercherò di fornire una risposta più chiara a questa domanda, per spiegare chiaramente quali sono le differenze tra dinamico ExpandoObjecte DynamicObject.

Molto rapidamente, dynamicè una parola chiave. Non è un tipo di per sé. È una parola chiave che dice al compilatore di ignorare il controllo del tipo statico in fase di progettazione e di utilizzare invece l'associazione tardiva in fase di esecuzione. Quindi non passeremo molto tempo dynamicnel resto di questa risposta.

ExpandoObjecte DynamicObjectsono davvero tipi. Sulla SUPERFICIE, sembrano molto simili tra loro. Entrambe le classi implementano IDynamicMetaObjectProvider. Tuttavia, scava più a fondo e scoprirai che NON sono affatto simili.

DynamicObject è un'implementazione parziale del IDynamicMetaObjectProviderpuro scopo di essere un punto di partenza per gli sviluppatori di implementare i propri tipi personalizzati che supportano l'invio dinamico con l'archiviazione sottostante personalizzata e il comportamento di recupero per far funzionare l'invio dinamico.

  1. DynamicObject non può essere costruito direttamente.
  2. DEVI estendere DynamicObject affinché possa servirti come sviluppatore.
  3. Quando estendi DynamicObject, sei ora in grado di fornire il comportamento PERSONALIZZATO riguardo al modo in cui desideri che il dispacciamento dinamico si risolva ai dati archiviati internamente nella rappresentazione dei dati sottostanti in fase di esecuzione.
  4. ExpandoObject archivia i dati sottostanti in un dizionario, ecc. Se si implementa DynamicObject, è possibile archiviare i dati ovunque e nel modo desiderato. (ad es. come ottenere e impostare i dati alla spedizione dipende interamente da te).

In breve, utilizzare DynamicObject quando si desidera creare i propri tipi PROPRI che possono essere utilizzati con il DLR e lavorare con qualsiasi comportamento CUSTOM desiderato.

Esempio: immagina di voler avere un tipo dinamico che restituisca un valore predefinito personalizzato ogni volta che si tenta di ottenere un membro su un membro che NON esiste (ovvero che non è stato aggiunto in fase di esecuzione). E quel default dirà "Mi dispiace, non ci sono biscotti in questo vaso!". Se vuoi un oggetto dinamico che si comporti in questo modo, dovrai controllare cosa succede quando non viene trovato un campo. ExpandoObject non ti consente di farlo. Quindi dovrai creare il tuo tipo con un comportamento unico di risoluzione (invio) dei membri dinamici e usarlo al posto di quello già pronto ExpandoObject.

È possibile creare un tipo come segue: (Nota, il codice seguente è solo a scopo illustrativo e potrebbe non essere eseguito. Per informazioni su come utilizzare correttamente DynamicObject, ci sono molti articoli e tutorial altrove.)

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;
    }
}

Ora, potremmo usare questa classe immaginaria che abbiamo appena creato come un tipo dinamico che ha un comportamento molto personalizzato se il campo non esiste.

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è un'implementazione COMPLETA di IDynamicMetaObjectProvider, in cui il team di .NET Framework ha preso tutte queste decisioni per te. Questo è utile se non hai bisogno di alcun comportamento personalizzato e ritieni che ExpandoObject funzioni abbastanza bene per te (il 90% delle volte ExpandoObjectè abbastanza buono). Ad esempio, vedere quanto segue e che per ExpandoObject, i progettisti hanno scelto di generare un'eccezione se il membro dinamico non esiste.

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) { ... }

Quindi, per riassumere, ExpandoObjectè semplicemente un modo prescelto per estendere DynamicObject con determinati comportamenti di spedizione dinamica che probabilmente funzioneranno per te , ma potrebbero non dipendere dalle tue esigenze particolari.

Considerando che, DyanmicObjectè un Helper BaseType che rende semplice e facile implementare i propri tipi con comportamenti dinamici unici.

Un tutorial utile su cui si basa gran parte della fonte di esempio sopra.


Ottima spiegazione Solo una correzione tecnica: ExpandoObject non eredita da DynamicObject.
Mike Rosoft,

Una piccola correzione nell'esempio per DynamicObject: quando TryGetMembersi RuntimeBinderExceptionesegue l' override , se si restituisce false a verrà generata quando si tenta di accedere a proprietà inesistenti. Affinché lo snippet funzioni effettivamente dovresti tornare true.
lluchmk,

36

Secondo la specifica del linguaggio C # dynamicè una dichiarazione di tipo. Vale a dynamic xdire che la variabile xha il tipo dynamic.

DynamicObjectè un tipo che semplifica l'implementazione IDynamicMetaObjectProvidere quindi sovrascrive il comportamento di associazione specifico per il tipo.

ExpandoObjectè un tipo che si comporta come una borsa di proprietà. Vale a dire è possibile aggiungere proprietà, metodi e così via alle istanze dinamiche di questo tipo in fase di esecuzione.


25
dynamicnon è un tipo reale ... è solo un suggerimento per dire al compilatore di usare l'associazione tardiva per questa variabile. dynamicle variabili sono effettivamente dichiarate come objectin MSIL
Thomas Levesque il

1
@Thomas: dal punto di vista del compilatore è un tipo, ma hai ragione che la rappresentazione di runtime è quella di Object. Troverai la frase "digitata staticamente come dinamica" in diverse presentazioni MS.
Brian Rasmussen,

3
@Thomas: e le specifiche del linguaggio dicono "C # 4.0 introduce un nuovo tipo statico chiamato dinamico".
Brian Rasmussen,

anzi ... Ma penso che sia confuso considerarlo come un tipo, poiché non esiste alcuna relazione di ereditarietà con tipi come DynamicObject o ExpandoObject
Thomas Levesque,

3
@NathanA Sono con te qui. Tuttavia, la specifica della lingua lo chiama un tipo, quindi è quello che sto andando con.
Brian Rasmussen,

0

L'esempio di cui sopra DynamicObjectnon dice chiaramente la differenza, perché fondamentalmente implementa la funzionalità già fornita da ExpandoObject.

Nei due collegamenti menzionati di seguito, è molto chiaro che, con l'aiuto di DynamicObject, è possibile conservare / modificare il tipo effettivo ( XElementnell'esempio utilizzato nei collegamenti seguenti) e un migliore controllo su proprietà e metodi.

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;    
        }    
    }    
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.