Quels sont les véritables avantages d'ExpandoObject?


587

The ExpandoObject classe ajoutée à .NET 4 vous permet de définir arbitrairement des propriétés sur un objet au moment de l'exécution.

Y a-t-il des avantages à utiliser un Dictionary<string, object>ou 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 légèrement plus succincte.

Par exemple, pourquoi est-ce:

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

Vraiment mieux, ou sensiblement différent, que:

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

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

Quels sont les avantages réels obtenus en utilisant ExpandoObject au lieu d'utiliser simplement un type de dictionnaire arbitraire, à part qu'il n'est pas évident que vous utilisez un type qui sera déterminé lors de l'exécution.

Réponses:


689

Depuis que j'ai écrit l'article MSDN auquel vous faites référence, je suppose que je dois répondre à celui-ci.

Tout d'abord, j'ai anticipé cette question et c'est pourquoi j'ai écrit un article de blog qui montre un cas d'utilisation plus ou moins réel pour ExpandoObject: Dynamique en C # 4.0: Présentation de ExpandoObject .

En bref, 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 la hiérarchie est profonde, plus le code est laid. 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);

Deuxièmement, comme cela a déjà été souligné, 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;

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

Gardez également à l'esprit que rien ne vous empêche d'accepter des arguments d'événement de manière dynamique. En d'autres termes, au lieu d'utiliser EventHandler, vous pouvez utiliser EventHandler<dynamic>ce qui entraînerait le deuxième argument du gestionnaire dynamic.


53
Intéressant. Merci pour les informations concernant les événements. C'était nouveau pour moi.
Reed Copsey

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

20
Peut-être que je manque quelque chose, mais ce n'est pas un événement - c'est une simple propriété de type délégué.
Sergey Berezovskiy

7
Le premier bloc peut être écrit en utilisant des types anonymes: var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);je trouve cela plus lisible mais ymmv. Et étant donné qu'il est typé statiquement, il est plus utile dans ce contexte.
nawfal

13
@nawfal ce n'est pas vrai - un anonyme est différent d'un Expando. Vous créez un type anonyme auquel vous ne pouvez pas ajouter de propriétés arbitraires.
Dr Blowhard

75

Un avantage est pour les scénarios contraignants. 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 comprendra 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 s'attendra à des propriétés DLR plutôt qu'à des entrées de dictionnaire, peut également être envisagée dans certains scénarios.


6
Il semble que la liaison de données à des objets dynamiques soit rompue . L'utilisateur déclarant eisenbergeffect est ici sur SO et coordinateur de caliburn.micro. @AlexandraRusina pouvez-vous commenter l'état du bug et le statut "Won't fix"
surfmuggle

2
Pour les curieux, je suis actuellement capable de me lier à WPF4 List<dynamic>et de l' IEnumerable<dynamic>utiliser
Graham Bass

47

Le véritable avantage pour moi est la liaison de données sans effort de XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

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

28

Interopérez avec d'autres langues fondées sur le DLR raison n ° 1 à laquelle 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 INotifyPropertyChangedce qui signifie que dans le monde de la liaison de données de WPF, il a également des avantages supplémentaires au-delà de ce qui Dictionary<K,V>peut vous fournir.


19

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


9
@J. Hendrix, n'oubliez pas qu'il a également dit "sale". Intellisense a ses inconvénients, cependant, il facilite le débogage et la capture de bogues. Personnellement, je préfère toujours les types statiques aux types dynamiques, sauf si je suis confronté à un cas étrange (et toujours rare).
Phil

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

1
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

14

Je pense que cela aura un avantage syntaxique, car vous ne serez plus en train de "simuler" des propriétés ajoutées dynamiquement en utilisant un dictionnaire.

Cela, et interagir avec des langues dynamiques, je pense.


11

C'est un exemple d'un excellent article MSDN sur l'utilisation d' ExpandoObject pour créer des types ad hoc dynamiques pour les données structurées entrantes (c'est-à-dire XML, Json).

Nous pouvons également affecter un délégué à la propriété dynamique d' ExpandoObject :

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 de la logique dans un objet dynamique lors de l'exécution. Par conséquent, avec les expressions lambda, les fermetures, les mots clés dynamiques et la classe DynamicObject , nous pouvons introduire certains éléments de programmation fonctionnelle dans notre code C #, que nous connaissons des langages dynamiques comme JavaScript ou PHP.


4

Il y a des cas où cela est pratique. Je vais l'utiliser pour un shell modularisé par exemple. Chaque module définit sa propre boîte de dialogue de configuration liée à ses paramètres. Je lui fournis un ExpandoObject car c'est Datacontext et enregistre les valeurs dans ma configuration Storage. De cette façon, le rédacteur de la boîte de dialogue de configuration n'a qu'à se lier à une valeur et il est automatiquement créé et enregistré. (Et fourni au module pour utiliser ces paramètres bien sûr)

Il est tout simplement plus facile à utiliser qu'un dictionnaire. Mais tout le monde doit savoir qu'en interne, il ne s'agit que d'un dictionnaire.

C'est comme LINQ juste du sucre syntaxique, mais cela facilite parfois les choses.

Donc, pour répondre directement à votre question: il est plus facile d'écrire et de lire. Mais techniquement, c'est essentiellement un Dictionary<string,object>(vous pouvez même le fondre en un pour répertorier les valeurs).


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

Je pense que cela ne fonctionne que parce que tout a un ToString (), sinon il faudrait connaître le type qu'il était et transtyper l '«objet» sur ce type.


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

  1. Il peut être beaucoup plus naturel d'accéder à une collection, dans ce cas ce qui est en fait un "dictionnaire", en utilisant la notation par points plus directe.

  2. Il semble que cela puisse être utilisé comme un tuple vraiment sympa. Vous pouvez toujours appeler vos membres "Item1", "Item2" etc ... mais maintenant vous n'avez plus à le faire, il est également modifiable, contrairement à un Tuple. Cela présente l'énorme inconvénient du manque de support intellisense.

  3. Vous pouvez être mal à l'aise avec les «noms de membres sous forme de chaînes», comme c'est le cas avec le dictionnaire, vous pouvez penser que c'est trop comme «exécuter des chaînes», et cela peut conduire à des conventions de dénomination codées et à traiter avec des morphèmes et syllabes lorsque le code essaie de comprendre comment utiliser les membres :-P

  4. Pouvez-vous attribuer une valeur à un ExpandoObject lui-même ou simplement à ses membres? Comparez et contrastez avec dynamique / dynamique [], utilisez celui qui convient le mieux à vos besoins.

  5. Je ne pense pas que dynamic / dynamic [] fonctionne dans une boucle foreach, vous devez utiliser var, mais vous pouvez peut-être utiliser ExpandoObject.

  6. Vous ne pouvez pas utiliser dynamic comme membre de données dans une classe, peut-être parce que c'est au moins un peu comme un mot-clé, espérons-le, vous pouvez le faire avec ExpandoObject.

  7. Je m'attends à ce qu'il "soit" un ExpandoObject, pourrait être utile pour étiqueter des choses très génériques, avec du code qui différencie en fonction des types où il y a beaucoup de choses dynamiques utilisées.


Soyez gentil si vous pouviez explorer plusieurs niveaux à la fois.

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

Ce n'est pas le meilleur exemple possible, imaginez des utilisations élégantes appropriées à vos propres projets.

C'est dommage que vous ne puissiez pas avoir du code pour en construire et pousser les résultats vers intellisense. Je ne sais pas comment cela fonctionnerait cependant.

Soyez gentils s'ils pouvaient avoir une valeur aussi bien que les membres.

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

-3

Après valueTuples, à quoi sert la classe ExpandoObject? ce code de 6 lignes 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 sur une ligne avec des tuples:

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

outre la syntaxe de tuple, vous avez une forte inférence de type et une prise en charge intelligente


1
Vos exemples ne sont pas identiques en ce sens qu'avec la valeur tuple, vous ne pouvez pas écrire Tc = 5; après avoir fini de définir T. Avec ExpandoObject, vous pouvez le faire car il est dynamique. Votre exemple avec la valeur tuple est très identique à la déclaration d'un type anonyme. Par exemple: var T2 = nouveau {x = 1, y = 2, z = nouveau {a = 3, b = 4}};
LxL

Pourquoi dois-je écrire Tc = 5 sans le définir? ExpandoObject n'est utile que lorsqu'il s'agit d'objets COM qui ne sont pas des démons en .net. Sinon, je n'utilise jamais cet ExpandoObject, car il est sale et bogué à la fois au moment de la conception et de l'exécution.
Eng. M.Hamdy

1
Que diriez-vous que z ait d'abord été assigné à (a: 3, b: 4) et ensuite vous aimeriez que z ait une propriété c supplémentaire? Pouvez-vous le faire avec un tuple de valeur?
LxL

Donc, mon point est que vous ne pouvez pas comparer ExpandoObject avec un tuple de valeur car ils sont conçus à des fins différentes. En comparant votre chemin, vous avez rejeté la fonctionnalité pour laquelle ExpandoObject est conçue, à savoir la structure dynamique.
LxL
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.