This is the final part of the
Dynamic Language Runtime series. Previous articles:
- 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.
- 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) {
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 :
- Can be used for rapid prototyping: in most cases, the number of boilerplate code is reduced
- As a rule, it improves readability and aesthetics (due to the transition from "magic lines" to the native style of the language) of the code
- Despite the widespread belief, thanks to caching mechanisms, a significant performance overhead in the general case does not arise
Cons of using dynamic:
- There are unobvious nuances associated with memory and performance.
- With the support and reading of such dynamic classes, you need to understand well what is going on
- The programmer is deprived of type checking and all health guarantees provided by the compiler
Conclusion
In my opinion, the developer will get the greatest profit from using dynamic in the following situations:
- When prototyping
- In small / home projects where the cost of error is low
- In small code-based utilities that do not imply a long runtime. If your utility executes in the worst case for a few seconds, there is usually no need to think about memory leaks and performance degradation
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.