Unterschiede zwischen ExpandoObject, DynamicObject und dynamic

170

Was sind die Unterschiede zwischen System.Dynamic.ExpandoObject, System.Dynamic.DynamicObjectunddynamic ?

In welchen Situationen verwenden Sie diese Typen?

M4N
quelle

Antworten:

154

Das dynamicSchlüsselwort wird verwendet, um Variablen zu deklarieren, die spät gebunden werden sollen.
Wenn Sie eine späte Bindung für einen realen oder imaginären Typ verwenden möchten, verwenden Sie diedynamic Schlüsselwort und der Compiler erledigt den Rest.

Wenn Sie das dynamicSchlüsselwort verwenden, um mit einer normalen Instanz, dem DLR, zu interagieren spät gebundene Aufrufe der normalen Methoden der Instanz durch.

Die IDynamicMetaObjectProviderSchnittstelle ermöglicht es einer Klasse, die Kontrolle über ihr spät gebundenes Verhalten zu übernehmen.
Wenn Sie das dynamicSchlüsselwort für die Interaktion mit einer IDynamicMetaObjectProviderImplementierung verwenden, ruft das DLR die IDynamicMetaObjectProviderMethoden auf und das Objekt selbst entscheidet, was zu tun ist.

Die ExpandoObjectund DynamicObjectKlassen sind Implementierungen von IDynamicMetaObjectProvider.

ExpandoObjectist eine einfache Klasse, mit der Sie Mitglieder zu einer Instanz hinzufügen und sie als dynamicVerbündeten verwenden können .
DynamicObjectist eine erweiterte Implementierung, die vererbt werden kann, um auf einfache Weise ein benutzerdefiniertes Verhalten bereitzustellen.

SLaks
quelle
2
Was wäre ein guter Ort, um mehr darüber zu erfahren? Nicht die API, sondern das Warum hinter der API? zB Warum leitet sich ExpandoObject nicht von DynamicObject ab, das den defacto-Basistyp für Rubys 'method_missing'-basierte Programmierung darstellt.
Gishu
4
Können Sie nach Möglichkeit einige Anwendungsbeispiele hinzufügen? Wie würde ich beispielsweise ein DynamicObject verwenden und welche Vorteile bietet es?
oɔɯǝɹ
10
Gute Antworten ohne solche Beispiele sind wie Kuchen ohne Sahne.
Teoman Shipahi
68

Ich werde versuchen, eine klarere Antwort auf diese Frage zu geben, um klar zu erklären, was die Unterschiede zwischen dynamisch ExpandoObjectund sindDynamicObject .

Sehr schnell dynamicist ein Schlüsselwort. Es ist kein Typ an sich. Es ist ein Schlüsselwort, das den Compiler anweist, die statische Typprüfung zur Entwurfszeit zu ignorieren und stattdessen zur Laufzeit die späte Bindung zu verwenden. Wir werden also dynamicim Rest dieser Antwort nicht viel Zeit damit verbringen .

ExpandoObjectund DynamicObjectsind in der Tat Typen. Auf der OBERFLÄCHE sehen sie einander sehr ähnlich. Beide Klassen implementieren IDynamicMetaObjectProvider. Wenn Sie jedoch tiefer graben, werden Sie feststellen, dass sie sich überhaupt nicht ähneln.

DynamicObject ist eine Teilimplementierung, die IDynamicMetaObjectProviderlediglich als Ausgangspunkt für Entwickler gedacht ist , um ihre eigenen benutzerdefinierten Typen zu implementieren, die den dynamischen Versand mit benutzerdefiniertem zugrunde liegenden Speicher- und Abrufverhalten unterstützen, damit der dynamische Versand funktioniert.

  1. DynamicObject kann nicht direkt erstellt werden.
  2. Sie MÜSSEN DynamicObject erweitern, damit es für Sie als Entwickler von Nutzen ist.
  3. Wenn Sie DynamicObject erweitern, können Sie jetzt das BENUTZERDEFINIERTE Verhalten angeben, wie der dynamische Versand zur Laufzeit in Daten aufgelöst werden soll, die intern in Ihrer zugrunde liegenden Datendarstellung gespeichert sind.
  4. ExpandoObject speichert zugrunde liegende Daten in einem Wörterbuch usw. Wenn Sie DynamicObject implementieren, können Sie Daten speichern, wo und wie Sie möchten. (zB wie Sie die Daten beim Versand erhalten und einstellen, liegt ganz bei Ihnen).

Kurz gesagt, verwenden Sie DynamicObject, wenn Sie Ihre EIGENEN Typen erstellen möchten, die mit dem DLR verwendet werden können, und mit den von Ihnen gewünschten CUSTOM-Verhaltensweisen arbeiten möchten.

Beispiel: Stellen Sie sich vor, Sie möchten einen dynamischen Typ haben, der einen benutzerdefinierten Standard zurückgibt, wenn versucht wird, ein Mitglied abzurufen, das NICHT vorhanden ist (dh zur Laufzeit nicht hinzugefügt wurde). Und diese Standardeinstellung lautet: "Es tut mir leid, in diesem Glas befinden sich keine Cookies!". Wenn Sie ein dynamisches Objekt möchten, das sich so verhält, müssen Sie steuern, was passiert, wenn ein Feld nicht gefunden wird. ExpandoObject lässt Sie dies nicht zu. Sie müssen also Ihren eigenen Typ mit einem eindeutigen Verhalten für die dynamische Elementauflösung (Versand) erstellen und diesen anstelle des vorgefertigten verwenden ExpandoObject.

Sie können einen Typ wie folgt erstellen: (Beachten Sie, dass der folgende Code nur zur Veranschaulichung dient und möglicherweise nicht ausgeführt wird. Um zu erfahren, wie Sie DynamicObject ordnungsgemäß verwenden, finden Sie an anderer Stelle viele Artikel und Tutorials.)

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

Jetzt können wir diese imaginäre Klasse, die wir gerade erstellt haben, als dynamischen Typ verwenden, der ein sehr benutzerdefiniertes Verhalten aufweist, wenn das Feld nicht vorhanden ist.

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

ExpandoObjectist eine vollständige Implementierung von IDynamicMetaObjectProvider, bei der das .NET Framework-Team all diese Entscheidungen für Sie getroffen hat. Dies ist nützlich, wenn Sie kein benutzerdefiniertes Verhalten benötigen und ExpandoObject Ihrer Meinung nach gut genug für Sie funktioniert (in 90% der Fälle ExpandoObjectist es gut genug). Sehen Sie sich zum Beispiel Folgendes an, und für ExpandoObject haben die Designer beschlossen, eine Ausnahme auszulösen, wenn das dynamische Element nicht vorhanden ist.

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

Zusammenfassend ExpandoObjectist dies einfach eine vorgewählte Möglichkeit, DynamicObject um bestimmte dynamische Versandverhalten zu erweitern, die wahrscheinlich für Sie funktionieren werden , jedoch möglicherweise nicht von Ihren speziellen Anforderungen abhängen.

Während DyanmicObjectist ein Helfer BaseType, der die Implementierung Ihrer eigenen Typen mit einzigartigen dynamischen Verhaltensweisen einfach und leicht macht.

Ein nützliches Tutorial, auf dem ein Großteil der obigen Beispielquelle basiert.

Ayo ich
quelle
Sehr gute Erklärung. Nur eine technische Korrektur: ExpandoObject erbt nicht von DynamicObject.
Mike Rosoft
Eine kleine Korrektur des Beispiels für DynamicObject: TryGetMemberWenn Sie beim Überschreiben false zurückgeben, RuntimeBinderExceptionwird beim Versuch, auf nicht vorhandene Eigenschaften zuzugreifen, a ausgelöst. Damit das Snippet tatsächlich funktioniert, sollten Sie zurückkehren true.
lluchmk
36

Gemäß der C # -Sprachenspezifikation dynamichandelt es sich um eine Typdeklaration. Dh dynamic xbedeutet, dass die Variable xden Typ hat dynamic.

DynamicObjectist ein Typ, der die Implementierung erleichtert IDynamicMetaObjectProviderund somit das spezifische Bindungsverhalten für den Typ überschreibt.

ExpandoObjectist ein Typ, der sich wie eine Eigenschaftstasche verhält. Das heißt, Sie können dynamischen Instanzen dieses Typs zur Laufzeit Eigenschaften, Methoden usw. hinzufügen.

Brian Rasmussen
quelle
25
dynamicist kein tatsächlicher Typ ... es ist nur ein Hinweis, den Compiler anzuweisen, die späte Bindung für diese Variable zu verwenden. dynamicVariablen werden tatsächlich wie objectin MSIL
Thomas Levesque
1
@Thomas: Aus Sicht des Compilers handelt es sich um einen Typ, aber Sie haben Recht, dass die Laufzeitdarstellung die von Object ist. Die Aussage "statisch typisiert, um dynamisch zu sein" finden Sie in mehreren MS-Präsentationen.
Brian Rasmussen
3
@Thomas: und die Sprachspezifikation sagt "C # 4.0 führt einen neuen statischen Typ namens dynamisch ein".
Brian Rasmussen
in der Tat ... Aber ich finde es verwirrend, es als Typ zu betrachten, da es keine Vererbungsbeziehung zu Typen wie DynamicObject oder ExpandoObject gibt
Thomas Levesque
3
@ NathanA Ich bin hier bei dir. Die Sprachspezifikation nennt es jedoch einen Typ, also gehe ich damit um.
Brian Rasmussen
0

Das obige Beispiel von DynamicObjectzeigt den Unterschied nicht klar, da es im Grunde die Funktionalität implementiert, die bereits von bereitgestellt wirdExpandoObject .

In den beiden unten genannten Links ist es sehr klar, dass es mit Hilfe von DynamicObjectmöglich ist, den tatsächlichen Typ beizubehalten / zu ändern (XElement in dem in den folgenden Links verwendeten Beispiel) und die Eigenschaften und Methoden besser zu steuern.

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
quelle