2009-10-31 2 views
493

La classe ExpandoObject aggiunta a .NET 4 consente di impostare arbitrariamente le proprietà su un oggetto in fase di esecuzione.Quali sono i veri vantaggi di ExpandoObject?

Ci sono dei vantaggi rispetto all'utilizzo di uno Dictionary<string,object> o addirittura di uno Hashtable? Per quanto posso dire, questo non è altro che una tabella hash alla quale è possibile accedere con una sintassi leggermente più sintetica.

Ad esempio, perché questo:

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

davvero meglio, o sostanzialmente differente, rispetto:

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

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

Nei reali vantaggi possono essere ottenuti utilizzando ExpandoObject invece di utilizzare un arbitrario tipo di dizionario, oltre a non essere ovvio che stai usando un tipo che verrà determinato in fase di runtime.

risposta

589

Da quando ho scritto l'articolo MSDN a cui si riferisce, suppongo di dover rispondere a questo.

In primo luogo, 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: Introducing the ExpandoObject.

In breve, ExpandoObject può aiutarti a creare oggetti gerarchici complessi. Per esempio, immaginate di avere un dizionario all'interno di un dizionario:

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"]); 

tanto più profonda è la gerarchia, la più brutta è 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 è stato già sottolineato, ExpandoObject implementa l'interfaccia INotifyPropertyChanged che vi dà più controllo sulle proprietà di un dizionario.

Infine, è possibile aggiungere eventi al 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); 
    } 
} 
+42

Interessante. Grazie per le informazioni su: eventi. Quello era nuovo per me. –

+4

@ Reed Sì, gli eventi non sono ancora documentati per ExpandoObject. Questo particolare esempio è venuto in realtà nella discussione sul blog. Probabilmente lo aggiungerò alla documentazione più tardi. –

+11

@AlexandraRusina, come fa a sapere che si tratta di un evento quando si dice 'd.MyEvent = null;', o no? – Shimmy

17

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

+8

@J. Hendrix, non dimenticare che ha anche detto "sporco". Intellisense ha il suo lato negativo, tuttavia, rende più facile il debugging e l'intercettazione di bug. Personalmente preferisco ancora tipi statici e dinamici, a meno che non mi occupi di un caso strano (e sempre raro). – Phil

+0

+1 per comodità. Tuttavia, trovo che i tipi anonimi possono essere ugualmente convenienti come un semplice sacchetto di proprietà e solo meglio per la sua staticità. – nawfal

+0

Non vorrei usarlo nel codice di produzione, ma è molto comodo nel test-code e può farlo apparire molto bello. – Tobias

64

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.

In alcuni scenari, l'interoperabilità con i linguaggi dinamici, che prevedono le proprietà DLR anziché le voci del dizionario, può essere presa in considerazione.

+14

+1 Databinding and Interop –

+5

[sembra che l'associazione di dati agli oggetti dinamici sia interrotta] (https://connect.microsoft.com/VisualStudio/feedback/details/522119/databinding-to-dynamic-objects-is-broken). L'utente segnalante eisenbergeffect è qui su SO e coordinatore di caliburn.micro. @AlexandraRusina puoi commentare lo stato del bug e lo stato "Non aggiustare" – surfmuggle

+0

Per chi è curioso, sono attualmente in grado di associare a 'Elenco ' e 'IEnumerable ' utilizzando WPF4 –

24

Interop con altre lingue basate su DLR è il motivo n. 1 a cui riesco a pensare. Non è possibile passare loro un Dictionary<string, object> in quanto non è un IDynamicMetaObjectProvider. Un altro vantaggio è che implementa INotifyPropertyChanged che significa nel mondo del database di WPF ha anche aggiunto vantaggi oltre a ciò che Dictionary<K,V> può fornire.

12

Penso che avrà un vantaggio sintattico, dal momento che non sarà più "finto" aggiungere dinamicamente le proprietà utilizzando un dizionario.

Questo, e interopro con linguaggi dinamici, penserei.

31

Il vero vantaggio per me sono i dati totalmente sforzo vincolante da XAML:

public dynamic SomeData { get; set; } 

...

SomeData.WhatEver = "Yo Man!"; 

...

<TextBlock Text="{Binding SomeData.WhatEver}" /> 
4

In alcuni casi è 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 facilita le cose.

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

8

E 'ad esempio dal grande MSDN article sull'utilizzo ExpandoObject per la creazione di tipi ad hoc dinamici per i dati strutturati in arrivo (cioè XML, JSON).

Possiamo anche assegnare delegato proprietà dinamica ExpandoObject s':

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

Così ci permette di iniettare una certa logica in oggetto dinamico in fase di esecuzione. Pertanto, insieme alle espressioni lambda, alle chiusure, alla parola chiave dinamica e allo DynamicObject class, possiamo introdurre alcuni elementi di programmazione funzionale nel nostro codice C#, che conosciamo da linguaggi dinamici come JavaScript o PHP.

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

penso che solo funziona perché ogni cosa ha un ToString(), altrimenti dovreste conoscere il tipo che era e il cast del 'oggetto' di quel tipo.


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

  1. Potrebbe essere molto più naturale per accedere ad una collezione, in questo caso ciò che è effettivamente un "dizionario", utilizzando la notazione punto più diretta.

  2. Sembra che questa possa essere usata 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. Si può essere a disagio con "nomi di membri come stringhe", come è il tatto con il dizionario, si potrebbe pensare che sia troppo simile a "eseguire stringhe" e che possa portare a convenzioni di denominazione che vengono codificate e che si occupano con il lavoro con morfemi e sillabe quando il codice cerca di capire come utilizzare i membri :-P

  4. È possibile 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 è possibile utilizzare dinamico come membro dati in una classe, forse perché è almeno una specie di parola chiave, si spera che sia possibile con ExpandoObject.

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


bello se si potesse drill-down più livelli in una sola volta.

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

Questo non è il miglior esempio possibile, si immagini usi eleganti come appropriato nei propri 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.

-1

Dopo valueTuples, a che serve la classe ExpandoObject? questo codice 6 linee con ExpandoObject:

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

può essere scritto in una linea con le tuple:

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

oltre che con la sintassi tuple avete forte inferenza di tipo e di sostegno intlisense

+0

Gli esempi non sono identici nel senso che con la tupla del valore, non è possibile scrivere T.c = 5; dopo aver terminato, definisci T. Con ExpandoObject puoi farlo perché è dinamico. L'esempio con tupla di valori è molto identico a quello della dichiarazione di tipo anonimo. E.g: var T2 = new {x = 1, y = 2, z = new {a = 3, b = 4}}; – LxL

+0

Perché devo scrivere T.c = 5 senza definirlo? ExpandoObject è utile solo quando si tratta di oggetti COM che non si trovano in .net. Altrimenti, non uso mai questo ExpandoObject, perché è sporco e buggato sia in fase di progettazione che in fase di esecuzione. –

+0

Che ne dici se hai z assegnato per la prima volta a (a: 3, b: 4) e successivamente vuoi che z abbia proprietà c aggiuntive? Puoi farlo con valore tuple? – LxL