Quali sono i veri vantaggi di ExpandoObject?

La class ExpandoObject che viene aggiunta a .NET 4 consente di impostare arbitrariamente le proprietà su un object in fase di esecuzione.

Ci sono dei vantaggi a questo proposito usando un Dictionary , o addirittura un Hashtable ? Per quanto posso dire, questo non è altro che una tabella hash alla quale è ansible accedere con una syntax leggermente più sintetica.

Ad esempio, perché è questo:

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

Davvero migliore, o sostanzialmente diverso, di:

 var obj = new Dictionary(); obj["MyInt"] = 3; obj["MyString"] = "Foo"; Console.WriteLine(obj["MyString"]); 

Quali sono i vantaggi reali ottenuti utilizzando ExpandoObject invece di utilizzare solo un tipo di dizionario arbitrario, oltre a non essere ovvio che si sta utilizzando un tipo che verrà determinato in fase di esecuzione.

Da quando ho scritto l’articolo MSDN a cui ti stai riferendo, credo di dover rispondere a questo.

Innanzitutto, ho anticipato questa domanda ed è per questo che ho scritto un post sul blog che mostra un caso d’uso più o meno reale per ExpandoObject: Dynamic in C # 4.0: Introduzione a ExpandoObject .

In breve, ExpandoObject può aiutarti a creare oggetti gerarchici complessi. Ad esempio, immagina di avere un dizionario all’interno di un dizionario:

 Dictionary dict = new Dictionary(); Dictionary address = new Dictionary(); dict["Address"] = address; address["State"] = "WA"; Console.WriteLine(((Dictionary)dict["Address"])["State"]); 

Più profonda è la gerarchia, più brutto è il codice. Con ExpandoObject rimane elegante e leggibile.

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

In secondo luogo, come già sottolineato, ExpandoObject implementa l’interfaccia INotifyPropertyChanged che offre un maggiore controllo sulle proprietà rispetto a un dizionario.

Infine, puoi aggiungere eventi a ExpandoObject come qui:

 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; if (e != null) { e(d, new EventArgs()); } // We could also fire it with... // d.MyEvent(d, new EventArgs()); // ...if we knew for sure that the event is non-null. } 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); } } 

Un vantaggio è per gli scenari vincolanti. Griglie di dati e griglie di proprietà raccoglieranno le proprietà dinamiche tramite il sistema TypeDescriptor. Inoltre, il binding dei dati WPF comprenderà le proprietà dinamiche, quindi i controlli WPF possono legarsi a ExpandoObject più facilmente di un dizionario.

L’interoperabilità con linguaggi dinamici, che si aspetteranno proprietà DLR piuttosto che voci di dizionario, potrebbe anche essere una considerazione in alcuni scenari.

Il vero vantaggio per me è l’associazione dati totalmente semplice di XAML:

 public dynamic SomeData { get; set; } 

 SomeData.WhatEver = "Yo Man!"; 

   

Interop con altri linguaggi basati sul DLR è la ragione numero 1 che riesco a pensare. Non è ansible passare loro un Dictionary poiché non è un IDynamicMetaObjectProvider . Un altro vantaggio è che implementa INotifyPropertyChanged che significa che nel mondo dei INotifyPropertyChanged di WPF ha anche aggiunto vantaggi oltre a quello che il Dictionary può fornire.

Si tratta di comodità del programmatore. Posso immaginare di scrivere programmi veloci e sporchi con questo object.

Penso che avrà un vantaggio sintattico, dal momento che non si potranno più “falsificare” le proprietà aggiunte dynamicmente usando un dizionario.

Questo, e interoperei con linguaggi dinamici, penserei.

È un esempio del grande articolo di MSDN sull’utilizzo di ExpandoObject per la creazione di tipi dinamici ad-hoc per i dati strutturati in entrata (ad es. XML, Json).

Possiamo anche assegnare un delegato alla proprietà dynamic di ExpandoObject :

 dynamic person = new ExpandoObject(); person.FirstName = "Dino"; person.LastName = "Esposito"; person.GetFullName = (Func)(() => { return String.Format("{0}, {1}", person.LastName, person.FirstName); }); var name = person.GetFullName(); Console.WriteLine(name); 

In questo modo ci permette di iniettare alcune logiche in oggetti dinamici in fase di runtime. Pertanto, insieme alle espressioni lambda, alle chiusure, alle parole chiave dinamiche e alla class DynamicObject , possiamo introdurre alcuni elementi di programmazione funzionale nel nostro codice C #, che conosciamo da linguaggi dinamici come JavaScript o PHP.

Ci sono alcuni casi in cui questo è utile. Lo userò per una shell Modularizzata, per esempio. Ogni modulo definisce la propria finestra di dialogo di configurazione associata alle sue impostazioni. Fornisco un ExpandoObject dato che è Datacontext e salvo i valori nella mia configurazione Storage. In questo modo il writer Dialog di configurazione deve solo associare a un valore e viene creato e salvato automaticamente. (E fornito al modulo per l’utilizzo di queste impostazioni ovviamente)

E ‘semplicemente più facile da usare di un dizionario. Ma tutti dovrebbero essere consapevoli che internamente è solo un dizionario.

È come LINQ solo zucchero sintattico, ma a volte rende le cose più facili.

Quindi, per rispondere direttamente alla tua domanda: è più facile da scrivere e più facile da leggere. Ma tecnicamente è essenzialmente un Dictionary (puoi persino lanciarlo in uno per elencare i valori).

 var obj = new Dictionary; ... Console.WriteLine(obj["MyString"]); 

Penso che funzioni solo perché tutto ha un ToString (), altrimenti dovresti sapere il tipo che era e lanciare l’object per quel tipo.


Alcuni di questi sono utili più spesso di altri, sto cercando di essere completo.

  1. Potrebbe essere molto più naturale accedere a una raccolta, in questo caso ciò che è effettivamente un “dizionario”, usando la notazione a punti più diretta.

  2. Sembra come se questo potesse essere usato come una bella tupla. Puoi ancora chiamare i tuoi membri “Item1”, “Item2”, ecc … ma ora non devi, è anche mutabile, a differenza di una Tupla. Questo ha l’enorme svantaggio della mancanza di supporto intellisense.

  3. Potresti sentirti a disagio con “nomi di membri come stringhe”, come è il tatto con il dizionario, potresti sentire che è troppo simile a “eseguire stringhe”, e potrebbe portare a convenzioni di denominazione che vengono codificate e ad occuparsi di lavorare con morfemi e sillabe quando il codice prova a capire come usare i membri 😛

  4. Puoi assegnare un valore a un ExpandoObject stesso o solo ai suoi membri? Confronta e metti in contrasto con dinamico / dinamico [], usa quello che meglio si adatta alle tue esigenze.

  5. Non penso che dynamic / dynamic [] funzioni in un ciclo foreach, devi usare var, ma probabilmente puoi usare ExpandoObject.

  6. Non è ansible utilizzare dinamico come membro dati in una class, forse perché è almeno una specie di parola chiave, si spera che sia ansible con ExpandoObject.

  7. Mi aspetto che “sia” un ExpandoObject, potrebbe essere utile etichettare cose molto generiche a parte, con un codice che si differenzia in base a tipi in cui sono in uso molte cose dinamiche.


Sii gentile se riesci a visualizzare più livelli contemporaneamente.

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

Questo non è il miglior esempio ansible, immagina usi eleganti come appropriato nei tuoi progetti.

È un peccato che non si possa avere codice per crearne alcuni e spingere i risultati all’intelligence. Non sono sicuro di come funzionerebbe.

Sii gentile se possono avere un valore così come i membri.

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

Dopo valueTuples, a che serve la class ExpandoObject? questo codice di 6 righe con ExpandoObject:

 dynamic T = new ExpandoObject(); Tx = 1; Ty = 2; Tz = new ExpandoObject(); Tza = 3; Tb= 4; 

può essere scritto in una riga con le tuple:

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

inoltre con la syntax della tupla hai una forte inferenza di tipo e il supporto intlisense