Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Connaissez-vous l’Expando ?

Le Framework .NET recèle des trésors. Souvent cachés, peu connus, ils peuvent se révéler de précieux auxiliaires pour se sortir de mauvaises passes. Connaissez-vous l’Expando ? Non ce n’est pas un gadget miraculeux vendu sur le web pour agrandir l’objet de votre virilité. Pas de ça ici. Alors c’est quoi ?

L’ExpandoObject

imageCaché dans System.Dynamic et totalement connecté à ce petit monde bien particulier, l’ExpandoObject est une classe bien facétieuse. Le genre de chose qu’on adore ou qu’on déteste, tout de suite, comme ça, sans réfléchir, instinctivement.

Le type Dynamic est arrivé avec .NET 4, nous en sommes à la 6ème version avec Roselyn et ces intéressantes nouveautés et notre ami l’expando est toujours inconnu…

Heureusement que je suis là pour combler vos lacunes !

 

C’est quoi ?

C’est un type. Donc en fait des instances. Comme Object. Au départ il n’est d’ailleurs pas plus utile que Object puisque ExpandoObject n’a ni propriété ni méthodes (en tout cas pas comme les autres classes qui “font quelque chose”).

Un doublon de Object ? En quelque sorte, mais à la sauve Dynamic…

C’est à dire que les instances de ExpandoObject peuvent se voire ajouter à tout moment, dynamiquement au runtime donc, des propriétés et mêmes des sortes de méthodes !

(ce magnifique poster de multivers peut s’acheter sur ciel et espace photos – crédit P.Carril / CEP)

Un bout de code

Prenons tout de suite un bon de code pour que vous compreniez l’affaire :

void Main()
{
	dynamic e = new ExpandoObject();
	e.FirstName = "Olivier";
	e.LastName = "Dahan";
	e.Print = new  Action( ()=> Console.WriteLine(e.FirstName+" "+e.LastName ));
	e.Print();
}

Etrange, non ?

L’instance “e” est donc un objet dynamic (il faut le définir clairement comme tel, un simple “var” ici ne marchera pas). Objet instancié par la création d’un nouvel ExpandoObject.

Jusque là rien de bien exotique malgré tout.

Mais que voit-on dans les lignes qui suivent ? On affecte une valeur à la propriété FirstName puis à LastName. Mais diable d’où sortent ces propriétés ?

De nulle part.

Vraiment. Nulle part. Elles sont créées dynamiquement.

Et la méthode Print() ? Elle est créée part l’instanciation d’une Action() qui contient le code à exécuter. Ce qui permet à la fin d’appeler e.Print() ce qui, vous l’avez deviné affichera glorieusement sur la console le nom de votre serviteur.

Utiliser la variable “e” à l’intérieur de l’action est un peu “cracra” je l’avoue. On pourrait choisir une écriture plus propre comme celle-ci :

void Main()
{
	dynamic e = new ExpandoObject();
	e.FirstName = "Olivier";
	e.LastName = "Dahan";
	e.Print = new  Action<dynamic>( (a)=> Console.WriteLine(a.FirstName+" "+a.LastName ));
	e.Print(e);
}

Du coup la méthode Print() prend désormais un paramètre de type dynamic. On peut peut lui passer “e” ou tout dynamic qui définirait les mêmes méthodes.

Vous avez compris le concept je suppose.

A quoi cela peut-il servir ?

.NET gère le boxing et l’unboxing depuis toujours. On pourrait donc écrire ce code en remplaçant e par un dictionnaire Dictionary<string,object> ce qui permettrait d’ajouter des “propriétés” (les clés du dictionnaire) et des valeurs (n’importe quoi puisqu’elles seront boxées sous la forme d’un Object).

Mais avouez que ça serait bien moins élégant comme écriture, plus verbeux. Et s’il faut manipuler plusieurs objets de ce type il faudra créer des dictionnaires de dictionnaires… Cela va vite devenir impossible à comprendre et à maintenir.

La maintenabilité et la lisibilité du code doivent primer sur toute autre considération dans un environnement professionnel. De très rares fois et pour des raisons qui restent peu nombreuses, les performances peuvent prendre le pas, dans une partie temps réel critique par exemple. Mais sinon maintenabilité et lisibilité passent avant tout.

Et aussi saugrenu que puisse paraitre l’ExpandoObject et sa loufoquerie congénitale – l’antéchrist caché dans un langage fortement typé qui accepte n’importe quoi reste un pied de nez intéressant aux sacro-saints principes de la programmation Object – et bien aussi farfelu que cela puisse sembler, l’expando peut aider à rendre le code plus lisible et plus facilement maintenable. Et c’est pour cela que je parle de cet orphelin caché au fond du Framework comme un enfant adultérin qu’on escamoterait à la populace rongé par la honte…

Non, n’aie pas honte mon petit expando, tu n’est certes pas très beau, un peu tordu, mais tu as le droit toi aussi à ton article dans Dot.Blog !

Donc soyons clairs, les dynamics ça passe ou ça casse. J’avais prévenu, on adore ou aime d’instinct. Haters gonna hate comme disent les ricains sur les rézossocios et qu’on traduirait par “les haineux vont haïr” ou “les rouspéteurs vont détester” selon la “violence” ressentie dans le contexte…

Autre exemple

Dans la réalité on peut se retrouver devant des structures complexes. Je parlais de dictionnaires de dictionnaires plus haut, c’est un bel exemple.

Imaginons le code suivant :

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

On a ici un dictionnaire de “choses” puis un dictionnaire d’adresses. Ce dernier étant répertorié dans le premier sous la clé “Address”. Puis on créée une valeur dans les adresses en ajoutant la clé “State” et sa valeur “TX”.

Si on veut atteindre cette valeur en partant du dictionnaire racine, ce qui est fait en dernière ligne, l’écriture est absolument imbuvable !

Imaginons maintenant un lecteur de Dot.Blog qui vient de passer par ici. Il écrira devant les yeux ébahis et les neurones médusés de ses collègues :

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

D’une horreur on obtient un code lisible. Fonctionnant de la même façon, tout aussi sophistiqué (des dictionnaires de dictionnaires ce n’est déjà plus très simple), mais clair. Donc maintenable.

Et mieux qu’un simple dictionnaire ExpandoObject() possède bien entendu des propriétés et des méthodes et même des évènements et des méthodes d’extension. Et parmi cette panoplie (qui le rend bien plus subtile qu’un simple Object) on trouve notre ami INPC dont j’ai parlé encore il y a peu.

Regardez ce code issu d’une réponse sur un forum de Alexandra Rusina qui avait écrit sur MSDN un article sur les Expando :

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

Saurez-vous prévoir la sortie ?

Ce sera :

OnMyEvent fired by: System.Dynamic.ExpandoObject
OnMyEvent2 fired by: System.Dynamic.ExpandoObject

Conclusion

L’expando (j’aime l’appeler par ce petit nom charmant aux consonances de héro vengeur latino-américain), ce brave petit n’est pas qu’une verrue atroce sur le nez grec de la programmation objet, il n’est pas cet enfant du malin mettant le vers gluant du non-typage dans la pomme fortement typée de C# pour le pervertir. Un peu quand même. Mais pas totalement.

Il peut exister des situations bien réelles où l’utilisation de l’expando rend le code plus lisible et plus maintenable.

Et puis on dispose d’une classe qui au runtime peut se construire petit à petit, propriétés, actions, support de INPC. Presque tout ce qu’il faut pour créer à la volée des ViewModels par exemple. Dans une architecture donnée qui suivrait un paramétrage complexe type gros ERP, cela pourrait même permettre d’écrire le code en 2 ans de moins peut-être… Des candidats pour détrôner SalesForce ou WaveSoft ?

En tout cas l’expando ne laisse pas indifférent. Et c’est bien.

Rester un bon développeur c’est surtout ne pas s’inscrire dans une routine qui endort les neurones. Douter, aimer, haïr, rester passionné donc, est l’aiguillon qui conserve l’esprit en éveil !

Stay Tuned !

blog comments powered by Disqus