Was sind die wahren Vorteile von ExpandoObject?

587

Mit der ExpandoObject- Klasse, die zu .NET 4 hinzugefügt wird, können Sie zur Laufzeit beliebig Eigenschaften für ein Objekt festlegen.

Gibt es irgendwelche Vorteile gegenüber der Verwendung eines Dictionary<string, object>oder sogar eines Hashtables ? Soweit ich das beurteilen kann, ist dies nichts anderes als eine Hash-Tabelle, auf die Sie mit etwas prägnanterer Syntax zugreifen können.

Warum ist das zum Beispiel so:

dynamic obj = new ExpandoObject();
obj.MyInt = 3;
obj.MyString = "Foo";
Console.WriteLine(obj.MyString);

Wirklich besser oder wesentlich anders als:

var obj = new Dictionary<string, object>();
obj["MyInt"] = 3;
obj["MyString"] = "Foo";

Console.WriteLine(obj["MyString"]);

Welche wirklichen Vorteile werden durch die Verwendung von ExpandoObject erzielt, anstatt nur einen beliebigen Wörterbuchtyp zu verwenden, außer dass nicht offensichtlich ist, dass Sie einen Typ verwenden, der zur Laufzeit festgelegt wird.

Reed Copsey
quelle

Antworten:

689

Da ich den MSDN-Artikel geschrieben habe, auf den Sie sich beziehen, muss ich diesen wohl beantworten.

Zuerst habe ich diese Frage vorweggenommen und deshalb einen Blog-Beitrag geschrieben, der einen mehr oder weniger realen Anwendungsfall für ExpandoObject: Dynamic in C # 4.0: Einführung des ExpandoObject zeigt .

In Kürze kann ExpandoObject Ihnen beim Erstellen komplexer hierarchischer Objekte helfen. Stellen Sie sich zum Beispiel vor, Sie haben ein Wörterbuch in einem Wörterbuch:

Dictionary<String, object> dict = new Dictionary<string, object>();
Dictionary<String, object> address = new Dictionary<string,object>();
dict["Address"] = address;
address["State"] = "WA";
Console.WriteLine(((Dictionary<string,object>)dict["Address"])["State"]);

Je tiefer die Hierarchie ist, desto hässlicher ist der Code. Mit ExpandoObject bleibt es elegant und lesbar.

dynamic expando = new ExpandoObject();
expando.Address = new ExpandoObject();
expando.Address.State = "WA";
Console.WriteLine(expando.Address.State);

Zweitens implementiert ExpandoObject, wie bereits erwähnt, die INotifyPropertyChanged-Schnittstelle, mit der Sie mehr Kontrolle über Eigenschaften haben als mit einem Wörterbuch.

Schließlich können Sie ExpandoObject wie folgt Ereignisse hinzufügen:

class Program
{
   static void Main(string[] args)
   {
       dynamic d = new ExpandoObject();

       // Initialize the event to null (meaning no handlers)
       d.MyEvent = null;

       // Add some handlers
       d.MyEvent += new EventHandler(OnMyEvent);
       d.MyEvent += new EventHandler(OnMyEvent2);

       // Fire the event
       EventHandler e = d.MyEvent;

       e?.Invoke(d, new EventArgs());
   }

   static void OnMyEvent(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent fired by: {0}", sender);
   }

   static void OnMyEvent2(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent2 fired by: {0}", sender);
   }
}

Denken Sie auch daran, dass Sie nichts daran hindert, Ereignisargumente dynamisch zu akzeptieren. Mit anderen Worten, anstatt zu verwenden EventHandler, können Sie verwenden, EventHandler<dynamic>was dazu führen würde, dass das zweite Argument des Handlers lautet dynamic.

Alexandra Rusina
quelle
53
Interessant. Danke für die Info zu: events. Das war neu für mich.
Reed Copsey
16
@AlexandraRusina, woher weiß es, dass es ein Ereignis ist, wenn du sagst d.MyEvent = null;, oder nicht?
Shimmy Weitzhandler
20
Vielleicht fehlt mir etwas, aber dies ist kein Ereignis - dies ist eine einfache Eigenschaft vom Typ Delegat.
Sergey Berezovskiy
7
Der erste Block kann mit anonymen Typen geschrieben werden: var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);Ich finde das besser lesbar, aber ymmv. Und da es statisch typisiert ist, ist es in diesem Zusammenhang nützlicher.
Nawfal
13
@nawfal das ist nicht richtig - ein Anonymer unterscheidet sich von einem Expando. Sie erstellen einen anonymen Typ, dem dann keine beliebigen Eigenschaften hinzugefügt werden können.
Dr. Blowhard
75

Ein Vorteil ist für Bindungsszenarien. Datenraster und Eigenschaftsraster erfassen die dynamischen Eigenschaften über das TypeDescriptor-System. Darüber hinaus versteht die WPF-Datenbindung dynamische Eigenschaften, sodass WPF-Steuerelemente leichter an ein ExpandoObject als an ein Wörterbuch gebunden werden können.

In einigen Szenarien kann auch die Interoperabilität mit dynamischen Sprachen in Betracht gezogen werden, bei der DLR-Eigenschaften anstelle von Wörterbucheinträgen erwartet werden.

itowlson
quelle
6
Es scheint, dass die Datenbindung zu dynamischen Objekten unterbrochen ist . Der berichtende Benutzer eisenbergeffect ist hier auf SO und Koordinator von caliburn.micro. @AlexandraRusina können Sie den Zustand des Fehlers und den Status "Wird nicht
behoben
2
Für diejenigen, die neugierig sind, bin ich derzeit in der Lage, an WPF4List<dynamic>IEnumerable<dynamic>
Graham Bass
47

Der eigentliche Vorteil für mich ist die völlig mühelose Datenbindung von XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

 <TextBlock Text="{Binding SomeData.WhatEver}" />
bjull
quelle
28

Interop mit anderen Sprachen, die auf dem DLRGrund basieren, den ich mir vorstellen kann. Sie können sie nicht weitergeben, Dictionary<string, object>da es keine ist IDynamicMetaObjectProvider. Ein weiterer zusätzlicher Vorteil ist die Implementierung, INotifyPropertyChangedwas bedeutet, dass es in der Datenbindungswelt von WPF auch zusätzliche Vorteile Dictionary<K,V>bietet, die über das hinausgehen, was Sie bieten können.

Drew Marsh
quelle
19

Es geht um Programmierkomfort. Ich kann mir vorstellen, mit diesem Objekt schnelle und schmutzige Programme zu schreiben.

ChaosPandion
quelle
9
@J. Hendrix, vergiss nicht, dass er auch "schmutzig" gesagt hat. Intellisense hat seine Schattenseiten, erleichtert jedoch das Debuggen und das Auffinden von Fehlern. Ich persönlich bevorzuge immer noch statische gegenüber dynamischen Typen, es sei denn, ich habe es mit einem seltsamen (und immer seltenen) Fall zu tun.
Phil
+1 für die Bequemlichkeit. Ich finde jedoch, dass anonyme Typen genauso praktisch sein können wie eine einfache Eigenschaftstasche und nur besser für ihre Statik.
Nawfal
1
Ich würde es nicht im Produktionscode verwenden wollen, aber es ist sehr praktisch im Testcode und kann es sehr schön aussehen lassen.
Tobias
14

Ich denke, es wird einen syntaktischen Vorteil haben, da Sie mithilfe eines Wörterbuchs keine dynamisch hinzugefügten Eigenschaften mehr "vortäuschen".

Das und Interop mit dynamischen Sprachen würde ich denken.

gn22
quelle
11

Es ist ein Beispiel aus einem großartigen MSDN-Artikel über die Verwendung von ExpandoObject zum Erstellen dynamischer Ad-hoc-Typen für eingehende strukturierte Daten (z. B. XML, Json).

Wir können der dynamischen Eigenschaft von ExpandoObject auch einen Delegaten zuweisen :

dynamic person = new ExpandoObject();
person.FirstName = "Dino";
person.LastName = "Esposito";

person.GetFullName = (Func<String>)(() => { 
  return String.Format("{0}, {1}", 
    person.LastName, person.FirstName); 
});

var name = person.GetFullName();
Console.WriteLine(name);

Auf diese Weise können wir zur Laufzeit eine Logik in ein dynamisches Objekt einfügen. Daher können wir zusammen mit Lambda-Ausdrücken, Abschlüssen, dynamischen Schlüsselwörtern und der DynamicObject-Klasse einige Elemente der funktionalen Programmierung in unseren C # -Code einführen, die wir aus dynamischen Sprachen wie JavaScript oder PHP kennen.

sgnsajgon
quelle
4

In einigen Fällen ist dies praktisch. Ich werde es zum Beispiel für eine modularisierte Shell verwenden. Jedes Modul definiert seinen eigenen Konfigurationsdialog, der an seine Einstellungen gebunden ist. Ich versorge es mit einem ExpandoObject als Datacontext und speichere die Werte in meinem Konfigurationsspeicher. Auf diese Weise muss der Konfigurationsdialogschreiber nur an einen Wert binden und wird automatisch erstellt und gespeichert. (Und natürlich dem Modul zur Verfügung gestellt, um diese Einstellungen zu verwenden)

Es ist einfach einfacher zu bedienen als ein Wörterbuch. Aber jeder sollte sich bewusst sein, dass es intern nur ein Wörterbuch ist.

Es ist wie LINQ nur syntaktischer Zucker, aber es macht die Dinge manchmal einfacher.

Um Ihre Frage direkt zu beantworten: Es ist einfacher zu schreiben und leichter zu lesen. Aber technisch gesehen ist es im Wesentlichen ein Dictionary<string,object>(Sie können es sogar in einen umwandeln, um die Werte aufzulisten).

n1LWeb
quelle
-1
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);

Ich denke, das funktioniert nur, weil alles einen ToString () hat, sonst müsste man den Typ kennen, der es war, und das 'Objekt' in diesen Typ umwandeln.


Einige davon sind öfter nützlich als andere, ich versuche gründlich zu sein.

  1. Es kann weitaus natürlicher sein, mit der direkteren Punktnotation auf eine Sammlung zuzugreifen, in diesem Fall auf ein "Wörterbuch".

  2. Es scheint, als könnte dies als ein wirklich schönes Tupel verwendet werden. Sie können Ihre Mitglieder weiterhin "Item1", "Item2" usw. nennen, aber jetzt müssen Sie nicht mehr, es ist im Gegensatz zu einem Tupel auch veränderlich. Dies hat den großen Nachteil, dass es an Intellisense-Unterstützung mangelt.

  3. Es kann sein, dass Sie sich mit "Mitgliedsnamen als Zeichenfolgen" nicht wohl fühlen, ebenso wie mit dem Wörterbuch. Sie haben möglicherweise das Gefühl, dass es zu ähnlich ist wie "Ausführen von Zeichenfolgen", und es kann dazu führen, dass Namenskonventionen codiert werden und mit Morphemen und Morphemen gearbeitet wird Silben, wenn Code versucht zu verstehen, wie man Mitglieder verwendet :-P

  4. Können Sie einem ExpandoObject selbst oder nur seinen Mitgliedern einen Wert zuweisen? Vergleichen und kontrastieren Sie mit dynamic / dynamic [] und verwenden Sie das, was Ihren Anforderungen am besten entspricht.

  5. Ich denke nicht, dass dynamic / dynamic [] in einer foreach-Schleife funktioniert. Sie müssen var verwenden, aber möglicherweise können Sie ExpandoObject verwenden.

  6. Sie können dynamic nicht als Datenelement in einer Klasse verwenden, vielleicht weil es zumindest wie ein Schlüsselwort ist, hoffentlich mit ExpandoObject.

  7. Ich gehe davon aus, dass es sich um ein ExpandoObject handelt, das nützlich sein kann, um sehr allgemeine Dinge mit Code zu kennzeichnen, der sich nach Typen unterscheidet, bei denen viele dynamische Dinge verwendet werden.


Seien Sie nett, wenn Sie mehrere Ebenen gleichzeitig aufschlüsseln könnten.

var e = new ExpandoObject();
e.position.x = 5;
etc...

Das ist nicht das bestmögliche Beispiel. Stellen Sie sich elegante Anwendungen vor, die in Ihren eigenen Projekten angemessen sind.

Es ist eine Schande, dass Sie nicht einige davon Code erstellen lassen und die Ergebnisse auf Intellisense übertragen können. Ich bin mir nicht sicher, wie das funktionieren würde.

Sei nett, wenn sie einen Wert haben könnten, genauso wie Mitglieder.

var fifteen = new ExpandoObject();
fifteen = 15;
fifteen.tens = 1;
fifteen.units = 5;
fifteen.ToString() = "fifteen";
etc...
alan2here
quelle
-3

Was nützt die ExpandoObject-Klasse nach valueTuples? Dieser 6-Zeilen-Code mit ExpandoObject:

dynamic T = new ExpandoObject();
T.x = 1;
T.y = 2;
T.z = new ExpandoObject();
T.z.a = 3;
T.b= 4;

kann in einer Zeile mit Tupeln geschrieben werden:

var T = (x: 1, y: 2, z: (a: 3, b: 4));

Außerdem haben Sie mit der Tupel-Syntax eine starke Typinferenz und Intlisense-Unterstützung

Eng. M. Hamdy
quelle
1
Ihre Beispiele sind nicht identisch in dem Sinne, dass Sie mit Wertetupel nicht Tc = 5 schreiben können; Nach Abschluss definieren Sie T. Mit ExpandoObject können Sie dies tun, weil es dynamisch ist. Ihr Beispiel mit dem Wertetupel ist sehr identisch mit der Deklaration eines anonymen Typs. Beispiel: var T2 = neu {x = 1, y = 2, z = neu {a = 3, b = 4}};
LxL
Warum muss ich Tc = 5 schreiben, ohne es zu definieren? ExpandoObject ist nur nützlich, wenn es sich um COM-Objekte handelt, die in .net nicht defiend sind. Ansonsten verwende ich dieses ExpandoObject nie, da es sowohl in der Entwurfszeit als auch zur Laufzeit fehlerhaft und fehlerhaft ist.
Eng. M.Hamdy
1
Wie wäre es, wenn Sie z zuerst (a: 3, b: 4) zugewiesen haben und später möchten, dass z zusätzliche c-Eigenschaften hat? Kannst du es mit Wertetupel machen?
LxL
Mein Punkt ist also, dass Sie ExpandoObject nicht mit Wertetupel vergleichen können, da sie für unterschiedliche Zwecke entwickelt wurden. Durch einen Vergleich haben Sie die Funktionalität, für die ExpandoObject entwickelt wurde, verworfen, nämlich eine dynamische Struktur.
LxL