Dynamic in C #: usage recipes

This is the final part of the Dynamic Language Runtime series. Previous articles:



  1. Dynamic in detail: compiler undercover games, memory leak, performance nuances . This article discusses in detail the DLR cache and the important moments for the developer related to it.
  2. Grokl DLR . A general overview of the technology, dissection of DynamicMetaObject and a short instruction on how to create your own dynamic class.


In this short article, we will finally analyze the main cases of using dynamic in real life: when you canโ€™t do without it and when it can make life much easier.







When dynamic is indispensable



There are no such cases. You can always write code similar in functionality in a static style, the only difference is the ease of reading and the amount of code. For example, when working with COM objects, instead of dynamic, you can use reflection.



When dynamic is useful



Work with COM objects



First of all, this, of course, is work with COM objects, for the sake of which all this was started. Compare the code obtained with dynamic and reflection:



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





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





As a rule, to work with COM objects through reflection, you have to create branchy classes with wrappers for each method / property. There are also less obvious goodies such as the ability not to fill in parameters you do not need (mandatory from the point of view of a COM object) when calling a method through dynamic .



Work with configs



Another textbook example is working with configs, such as XML . Without dynamic :



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





With dynamic:



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





Of course, for this you need to implement your own dynamic class. As an alternative to the first listing, you can write a class that will work something like this:



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





But, you see, this looks much less elegant than through dynamic .



Work with external resources



The previous paragraph can be generalized to any actions with external resources. We always have two alternatives: using dynamic to get the code in native C # style or static typing with โ€œmagic linesโ€. Let's look at an example with a REST API request. With dynamic, you can write this:



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





Where our dynamic class will send a request of the form at the request of the property



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





Then he deserializes it and returns to us an array of cats that are ready for the intended use. Without dynamic, it would look something like this:



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





Reflection Replacement



In the previous example, you might have a question: why in one case we are deserializing the return value to a specific type, and in the other not? The fact is that in static typing we need to explicitly cast objects to the Cat type to work with them. In the case of dynamic , it is enough to deserialize JSON into an array of objects inside our dynamic class and return object [] from it, since dynamic takes care of reflection. I will give two examples of how this works:



 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;
      
      





The same principle as when working with COM objects.



Visitor



With dynamic, you can very elegantly implement this pattern. Instead of a thousand words:



 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 {}
      
      





Now, when passing an object of type Sword to the DoSomeWork method, the InternalDoSomeWork (Sword item) method will be called.



findings



Pros of using dynamic :





Cons of using dynamic:





Conclusion



In my opinion, the developer will get the greatest profit from using dynamic in the following situations:





At least controversial is the use of dynamic in complex projects with a large code base - here it is better to spend time writing static wrappers, thus minimizing the number of unobvious moments.



If you work with COM objects or domains in services / products that imply a long continuous working time, it is better not to use dynamic , despite the fact that it was created for such cases. Even if you thoroughly know what and how to do and never make mistakes, sooner or later a new developer may come who does not know this. The result is likely to be a hard-to-compute memory leak.



All Articles