C # dynamic type causes Console.WriteLine to resolve with reflection in IL

I played with some C # statements in LINQPad to figure out what the middle language code emits.

First I tried the following code:

var Container = new {Name = "James"}; Console.WriteLine(Container.Name); 

And I saw the following six lines of IL emitted:

 IL_0001: ldstr "James" IL_0006: newobj <>f__AnonymousType0<System.String>..ctor IL_000B: stloc.0 IL_000C: ldloc.0 IL_000D: callvirt <>f__AnonymousType0<System.String>.get_Name IL_0012: call System.Console.WriteLine 

Which, in general, I expect, and a pretty nice demonstration of how anonymous types are read-only / immutable, since the set_Name property is missing.

Next I tried the instructions:

 dynamic Container = new System.Dynamic.ExpandoObject(); Container.Name = "James"; Console.WriteLine(Container.Name); 

Because of this, a huge amount of IL occurs. I will not embed it here, but you can find it in this pastebin .

I understand that there is quite a lot of overhead to managing the dynamic type and ExpandoObject, but I don’t understand why it seems that the call to System.Console.WriteLine in this case is done through internal reflection.

 IL_0072: ldstr "WriteLine" .... IL_00BF: ldtoken System.Console 

In the first code segment, after the property was restored and saved, it was an IL single-line statement that called System.Console.WriteLine .

So why is all this necessary for a call of type dynamic ?

+6
source share
2 answers

Since the variable is dynamic , at compile time it is not possible to find out which WriteLine overload should be called. Only before execution do we know the actual type of the dynamic object. Because of how dynamic works, it is important that at compile time it is not just treated as an object ; part of the power is that it determines the correct overload at runtime.

If you pass the object to something other than dynamic (i.e. string after calling ToString or just go back to ExpandoObject ) and then pass it to WriteLine , then you should see that the reflection call goes away and sees it statically determines at compile time proper overload of WriteLine .

+7
source

What happens, the compiler creates your code so that it can be "late." Late binding means that instead of resolving your objects at compile time, as with traditional data types and objects, the object is solved at runtime, while your assembly is actually in memory and running.

If you look at your code in Reflector or dotPeek, you will see that your dynamic objects have been decorated with the [Dynamic] attribute. While your program is running in memory, when it comes to an object that has been decorated with this attribute, a call to that object is passed through a dynamic Container (or any other object that you call). This Container initialized using the Binder responsible for binding the runtime. This is what Microsoft.CSharp.RuntimeBinder all calls. This RuntimeBinder used later to call properties or methods or something dynamic.

Hope this makes things a bit easier. I am typing on my android, so the explanation may be less than ideal. I will clean it later.

+1
source

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


All Articles