2009-10-31 4 views
493

La classe ExpandoObject en cours d'ajout à .NET 4 vous permet de définir arbitrairement des propriétés sur un objet lors de l'exécution.Quels sont les véritables avantages d'ExpandoObject?

Y at-il des avantages à cela sur l'utilisation d'un Dictionary<string,object>, ou vraiment même un Hashtable? Pour autant que je sache, ce n'est rien d'autre qu'une table de hachage à laquelle vous pouvez accéder avec une syntaxe un peu plus succincte.

Par exemple, pourquoi est-ce:

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

vraiment mieux, ou substantiellement différent, que:

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

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

Quels réels avantages sont obtenus en utilisant ExpandoObject au lieu d'utiliser un arbitraire type de dictionnaire, autre que n'étant pas évident que vous utilisez un type qui va être déterminé au moment de l'exécution.

Répondre

589

Depuis que j'ai écrit l'article MSDN auquel vous faites référence, je suppose que je dois répondre à celui-ci. D'abord, j'ai anticipé cette question et c'est pourquoi j'ai écrit un billet de blog qui montre un cas d'utilisation plus ou moins réel pour ExpandoObject: Dynamic in C# 4.0: Introducing the ExpandoObject.

En peu de mots, ExpandoObject peut vous aider à créer des objets hiérarchiques complexes. Par exemple, imaginez que vous avez un dictionnaire dans un dictionnaire:

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

plus profonde est la hiérarchie, le plus laid est le code. Avec ExpandoObject, il reste élégant et lisible.

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

En second lieu, comme il a été déjà signalé, ExpandoObject implémente l'interface INotifyPropertyChanged qui vous donne plus de contrôle sur les propriétés qu'un dictionnaire.

Enfin, vous pouvez ajouter des événements à ExpandoObject comme ici:

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

Intéressant. Merci pour l'info re: les événements. C'était nouveau pour moi. –

+4

@ Reed Oui, les événements ne sont pas encore documentés pour ExpandoObject. Cet exemple particulier est apparu dans la discussion sur le blog. Je vais probablement l'ajouter à la documentation plus tard. –

+11

@AlexandraRusina, comment sait-il que c'est un événement quand vous dites 'd.MyEvent = null;', ou pas? – Shimmy

17

Tout est une question de commodité pour les programmeurs. Je peux imaginer écrire des programmes rapides et sales avec cet objet.

+8

@J. Hendrix, n'oublie pas qu'il a aussi dit "sale". Intellisense a ses inconvénients, cependant, il facilite le débogage et la capture d'erreurs. Personnellement, je préfère encore les types statiques aux dynamiques à moins que je ne m'occupe d'un cas bizarre (et toujours rare). – Phil

+0

+1 pour plus de commodité. Cependant, je trouve que les types anonymes peuvent être aussi pratiques qu'un sac de propriété simple et tout simplement meilleur pour son caractère statique. – nawfal

+0

Je ne voudrais pas l'utiliser dans le code de production, mais il est très pratique dans le code de test et peut le rendre très beau. – Tobias

64

Un avantage est pour les scénarios de liaison. Les grilles de données et les grilles de propriétés récupèrent les propriétés dynamiques via le système TypeDescriptor. En outre, la liaison de données WPF comprend les propriétés dynamiques, de sorte que les contrôles WPF peuvent se lier à un ExpandoObject plus facilement qu'un dictionnaire.

L'interopérabilité avec les langages dynamiques, qui attendront des propriétés DLR plutôt que des entrées de dictionnaire, peut également être prise en compte dans certains scénarios.

+14

+1 Liaison de données et interop –

+5

Il semble que la liaison de données aux objets dynamiques soit rompue. (Https://connect.microsoft.com/VisualStudio/feedback/details/522119/databinding-to-dynamic-objects-is-broken). L'utilisateur de reporting eisenbergeffect est ici sur SO et coordinateur de caliburn.micro. @AlexandraRusina pouvez-vous commenter sur l'état du bug et le statut "Ne corrigera pas" – surfmuggle

+0

Pour les curieux, je suis actuellement capable de lier à 'List ' et 'IEnumerable ' en utilisant WPF4 –

24

L'interopérabilité avec d'autres langues fondée sur le DLR est la raison # 1 que je peux penser. Vous ne pouvez pas leur passer un Dictionary<string, object> car ce n'est pas un IDynamicMetaObjectProvider. Un autre avantage supplémentaire est qu'il implémente INotifyPropertyChanged ce qui signifie que dans le monde de la liaison de données de WPF, il a également ajouté des avantages au-delà de ce que Dictionary<K,V> peut vous fournir.

12

Je pense que cela aura un avantage sur le plan syntaxique, puisque vous ne ferez plus de "fausses" propriétés ajoutées dynamiquement en utilisant un dictionnaire.

Cela, et d'interagir avec les langues dynamiques, je pense.

31

Le véritable avantage pour moi est tout à fait sans effort les données de liaison de XAML:

public dynamic SomeData { get; set; } 

...

SomeData.WhatEver = "Yo Man!"; 

...

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

Il existe certains cas où cela est pratique. Je vais l'utiliser pour un shell modulaire par exemple. Chaque module définit sa propre boîte de dialogue de configuration avec ses paramètres. Je le fournis avec un ExpandoObject comme Datacontext et enregistre les valeurs dans ma configuration Storage. De cette façon, le rédacteur de dialogue de configuration doit juste se lier à une valeur et il est automatiquement créé et enregistré. (Et fourni au module pour l'utilisation de ces paramètres bien sûr)

Il est simplement plus facile à utiliser qu'un dictionnaire. Mais tout le monde devrait savoir qu'en interne, c'est juste un dictionnaire. C'est comme du sucre syntaxique LINQ, mais cela rend les choses plus faciles parfois. Donc, pour répondre directement à votre question: c'est plus facile à écrire et plus facile à lire. Mais techniquement, il s'agit essentiellement d'un Dictionary<string,object> (vous pouvez même le jeter en un pour lister les valeurs).

8

Il est par exemple d'une grande MSDN article sur l'utilisation ExpandoObject pour la création de types ad hoc dynamiques pour les données structurées entrants (i.e. XML, JSON).

Nous pouvons également attribuer délégué ExpandoObject de propriété dynamique:

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

Ainsi, il nous permet d'injecter une certaine logique en objet dynamique lors de l'exécution. Par conséquent, avec les expressions lambda, les fermetures, le mot-clé dynamique et DynamicObject class, nous pouvons introduire quelques éléments de programmation fonctionnelle dans notre code C#, que nous connaissons depuis des langages dynamiques comme le JavaScript ou le PHP.

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

Je pense que ne fonctionne que parce que tout a un ToString(), sinon vous auriez à connaître le type qu'il était et jeté « objet » à ce type.


Certains d'entre eux sont utiles plus souvent que d'autres, j'essaie d'être approfondie.

  1. Il peut être beaucoup plus naturel pour accéder à une collection, dans ce cas, ce qui est effectivement un « dictionnaire », en utilisant la notation dot plus directe.

  2. Il semble que cela pourrait être utilisé comme un très bon Tuple. Vous pouvez toujours appeler vos membres "Item1", "Item2" etc ... mais maintenant vous ne devez pas, c'est aussi mutable, contrairement à un Tuple. Cela a l'énorme inconvénient du manque de soutien intellisense.Vous pouvez être mal à l'aise avec les "noms de membres en tant que chaînes", comme c'est le sens du dictionnaire, vous pouvez penser que c'est trop "exécuter des chaînes", et cela peut conduire à des conventions de nommage codées et traitant Travailler avec des morphèmes et des syllabes lorsque le code essaie de comprendre comment utiliser les membres: -P

  3. Pouvez-vous attribuer une valeur à un ExpandoObject ou à ses membres? Comparez et contrastez dynamique/dynamique [], utilisez celui qui répond le mieux à vos besoins.

  4. Je ne pense pas que dynamic/dynamic [] fonctionne dans une boucle foreach, vous devez utiliser var, mais vous pouvez éventuellement utiliser ExpandoObject.

  5. Vous ne pouvez pas utiliser Dynamic en tant que membre de données dans une classe, peut-être parce que c'est au moins un mot-clé, espérons-le avec ExpandoObject. Je suppose que c'est "un" ExpandoObject, peut-être utile d'étiqueter des choses très génériques, avec du code qui se différencie en fonction des types où il y a beaucoup de choses dynamiques utilisées.


bien si vous pouvez percer les niveaux multiples à la fois.

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

Ce n'est pas le meilleur exemple possible, imaginez des utilisations élégantes comme il se doit dans vos propres projets.

Dommage que le code ne puisse pas en construire certains et pousser les résultats à intelense. Je ne suis pas sûr comment cela fonctionnerait bien.

Soyez gentil s'ils peuvent avoir une valeur aussi bien que des membres.

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

Après valueTuples, à quoi sert la classe ExpandoObject? ce 6 lignes de code avec ExpandoObject:

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

peut être écrit en une ligne avec tuples:

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

en plus avec la syntaxe tuple vous avez une forte inférence de type et de soutien intlisense

+0

Vos exemples ne sont pas identiques dans le sens où avec la valeur tuple, vous ne pouvez pas écrire T.c = 5; après avoir fini de définir T. Avec ExpandoObject vous pouvez le faire parce que c'est dynamique. Votre exemple avec la valeur tuple est très identique à la déclaration de type anonyme. E.g: var T2 = nouveau {x = 1, y = 2, z = nouveau {a = 3, b = 4}}; – LxL

+0

Pourquoi ai-je besoin d'écrire T.c = 5 sans le définir? ExpandoObject n'est utile que pour les objets COM qui ne sont pas définis dans .net. Sinon, je n'utilise jamais ce ExpandoObject, car il est sale et bogué à la fois au moment du design et de l'exécution. –

+0

Qu'en est-il de z assigné d'abord à (a: 3, b: 4) et plus tard, vous aimeriez que z ait une propriété c supplémentaire? Pouvez-vous le faire avec un tuple de valeur? – LxL

Questions connexes