Dynamique en C #: recettes d'utilisation

Il s'agit de la dernière partie de la série Dynamic Language Runtime . Articles précédents:

  1. Détail dynamique: jeux d'infiltration du compilateur, fuite de mémoire, nuances de performances . Cet article décrit le cache DLR en détail et les points importants pour le développeur.
  2. Grokl DLR . Un aperçu général de la technologie, la dissection de DynamicMetaObject et une brève instruction sur la façon de créer votre propre classe dynamique.

Dans ce court article, nous analyserons enfin les principaux cas d'utilisation de la dynamique dans la vie réelle: quand on ne peut plus s'en passer et quand cela peut grandement faciliter l'existence.



Quand la dynamique est indispensable


Il n'y a pas de tels cas. Vous pouvez toujours écrire du code de fonctionnalité similaire dans un style statique, la seule différence est la facilité de lecture et la quantité de code. Par exemple, lorsque vous travaillez avec des objets COM, au lieu de dynamiques, vous pouvez utiliser la réflexion.

Quand la dynamique est utile


Travailler avec des objets COM


Tout d'abord, bien sûr, c'est un travail avec des objets COM, pour le plaisir de commencer. Comparez le code obtenu avec dynamique et réflexion:

dynamic instance = Activator.CreateInstance(type); instance.Run("Notepad.exe"); 

 var instance = Activator.CreateInstance(type); type.InvokeMember("Run", BindingFlags.InvokeMethod, null, instance, new[] { "Notepad.exe" }); 

En règle générale, pour travailler avec des objets COM par réflexion, vous devez créer des classes branchées avec des wrappers pour chaque méthode / propriété. Il existe également des avantages moins évidents tels que la possibilité de ne pas remplir les paramètres dont vous n'avez pas besoin (obligatoire du point de vue d'un objet COM) lors de l'appel d'une méthode via Dynamic .

Travailler avec des configurations


Un autre exemple de manuel fonctionne avec des configurations, telles que XML . Sans dynamique :

 XElement person = XElement.Parse(xml); Console.WriteLine( $"{person.Descendants("FirstName").FirstOrDefault().Value} {person.Descendants("LastName").FirstOrDefault().Value}" ); 

Avec dynamique:

 dynamic person = DynamicXml.Parse(xml); Console.WriteLine( $"{person.FirstName} {person.LastName}" ); 

Bien sûr, pour cela, vous devez implémenter votre propre classe dynamique. Comme alternative à la première liste, vous pouvez écrire une classe qui fonctionnera comme ceci:

 var person = StaticXml.Parse(xml); Console.WriteLine( $"{person.GetElement("FirstName")} {person.GetElement("LastName")}" ); 

Mais, vous voyez, cela semble beaucoup moins élégant que dynamique .

Travailler avec des ressources externes


Le paragraphe précédent peut être généralisé à toutes les actions avec des ressources externes. Nous avons toujours deux alternatives: utiliser dynamique pour obtenir le code en style C # natif ou taper statique avec des «lignes magiques». Regardons un exemple avec une demande d' API REST . Avec dynamic, vous pouvez écrire ceci:

 dynamic dynamicRestApiClient = new DynamicRestApiClient("http://localhost:18457/api"); dynamic catsList = dynamicRestApiClient.CatsList; 

Où notre classe dynamique enverra une demande de formulaire à la demande de la propriété

 [GET] http://localhost:18457/api/catslist 

Puis il le désérialise et nous retourne un ensemble de chats déjà prêts pour l'usage prévu. Sans dynamique, cela ressemblerait à ceci:

 var restApiClient = new RestApiClient("http://localhost:18457/api"); var catsListJson = restApiClient.Get("catsList"); var deserializedCatsList = JsonConvert.DeserializeObject<Cat[]>(catsListJson); 

Remplacement de la réflexion


Dans l'exemple précédent, vous pourriez avoir la question: pourquoi dans un cas, nous désérialisons la valeur de retour en un type spécifique, et dans l'autre non? Le fait est que dans le typage statique, nous devons explicitement convertir des objets en type Cat pour travailler avec eux. Dans le cas de la dynamique , il suffit de désérialiser JSON en un tableau d'objets à l'intérieur de notre classe dynamique et d'en renvoyer l' objet [] , car la dynamique prend soin de la réflexion. Je vais donner deux exemples de comment cela fonctionne:

 dynamic deserialized = JsonConvert.DeserializeObject<object>(serialized); var name = deserialized.Name; var lastName = deserialized.LastName; 

 Attribute[] attributes = type.GetCustomAttributes(false).OfType<Attribute>(); dynamic attribute = attributes.Single(x => x.GetType().Name == "DescriptionAttribute"); var description = attribute.Description; 

Le même principe que lorsque vous travaillez avec des objets COM.

Visiteur


En utilisant dynamique, vous pouvez implémenter ce modèle de manière très élégante. Au lieu de mille mots:

 public static void DoSomeWork(Item item) { InternalDoSomeWork((dynamic) item); } private static void InternalDoSomeWork(Item item) { throw new Exception("Couldn't find handler for " + item.GetType()); } private static void InternalDoSomeWork(Sword item) { //do some work with sword } private static void InternalDoSomeWork(Shield item) { //do some work with shield } public class Item { } public class Sword : Item {} public class Shield : Item {} 

Désormais, lors du passage d'un objet de type Sword à la méthode DoSomeWork , la méthode InternalDoSomeWork (élément Sword) sera appelée.

Conclusions


Avantages de l'utilisation dynamique :

  • Peut être utilisé pour le prototypage rapide: dans la plupart des cas, le nombre de codes passe-partout diminue
  • En règle générale, il améliore la lisibilité et l'esthétique (en raison de la transition des «lignes magiques» au style natif de la langue) du code
  • Malgré l'opinion répandue, grâce aux mécanismes de mise en cache, un surdébit de performances significatif dans le cas général ne se pose pas

Inconvénients de l'utilisation dynamique:

  • Il existe des nuances évidentes associées à la mémoire et aux performances.
  • Avec le soutien et la lecture de ces classes dynamiques, vous devez bien comprendre ce qui se passe
  • Le programmeur est privé de vérification de type et de toutes les garanties de santé fournies par le compilateur

Conclusion


À mon avis, le développeur tirera le plus grand profit de l'utilisation de Dynamic dans les situations suivantes:

  • Lors du prototypage
  • Dans les petits projets / projets à domicile où le coût d'erreur est faible
  • Dans les petits utilitaires de taille de code qui n'impliquent pas une longue durée. Si votre utilitaire s'exécute dans le pire des cas pendant quelques secondes, il n'est généralement pas nécessaire de penser aux fuites de mémoire et à la dégradation des performances

Au moins controversé est l'utilisation de la dynamique dans des projets complexes avec une grande base de code - ici, il est préférable de passer du temps à écrire des wrappers statiques, minimisant ainsi le nombre de moments non évidents.

Si vous travaillez avec des objets ou des domaines COM dans des services / produits qui impliquent un long temps de travail continu, il est préférable de ne pas utiliser dynamique , malgré le fait qu'il a été créé pour de tels cas. Même si vous savez parfaitement quoi et comment faire et ne faites jamais d'erreurs, tôt ou tard un nouveau développeur peut arriver qui ne le sait pas. Le résultat est probablement une fuite de mémoire difficile à calculer.

Source: https://habr.com/ru/post/fr470355/


All Articles