The article takes you to understand the world of C # DLR

Time:2021-2-25

A long time ago, I wrote an article to explain C # anonymous object (anonymous type), VaR and dynamic type in detail, which can be used for reference. At that time, I took it for granted that only reflection could parse the member information of an object and call member methods at runtime. Later, because of other things, I didn’t come back to make up for this section of knowledge. It’s just the so-called mending after a sheep has died. Let’s have a general understanding of DLR now.

The full name of DLR is dynamic language runtime. It’s easy to think of another thing called CLR in C #, which is called common language runtime. What’s the relationship between the two? I’ll talk about it later

The dynamic function of cා4 is a part of dynamic language runtime (DLR). DLR is a series of services added to CLR. It allows adding dynamic languages, such as ruby and python, and enables cාto have some of the same functions as these dynamic languages

DLR is a new concept introduced from C # 4.0, and its main purpose is dynamic binding and interaction.

C # keyword dynamic

DLR first defines a core type concept, namely dynamic type. That is, the type determined at run time, the member information and methods of dynamic type are bound only at run time. In contrast to CLR static types, static types are matched to the final binding through a series of rules during C # compilation.

This dynamic binding process is a bit like reflection, but its interior is very different from reflection. I’ll talk about that a little bit.

An object composed of dynamic types is called a dynamic object.

DLR generally has the following characteristics:

  • Implicitly convert all types of CLR todynamic. asdynamic x = GetReturnAnyCLRType()
  • Similarly, dynamic can almost be converted to CLR types.
  • All expressions with dynamic types are dynamically evaluated at runtime.

With the development of DLR, we almost all use dynamic type keywordsdynamicAs well as the class library dapper that references DLR.

When we don’t want to create new static classes for dto mapping, we will think of dynamic types for the first time. Dynamic is often used as a parameter.

At this time, we need to pay attention to some details that dynamic is not known to most people.

Not all expressions that contain dynamic are dynamic.

What do you mean? Let’s see the codedynamic x = "marson shine";. This code is very simple, that is to assign a string to the dynamic type X.

Don’t think that this is a dynamic type. In fact, it’s not. If it’s just this sentence, the C # compiler will change the variable x into a static type object during compilation, which is equivalent toobject x = "marson shine";. Some people may wonder why the C # compiler finally generates object type code. That’s what we need to pay attention to next.

The invisible relationship between dynamic and object

In fact, if you take the dynamic type as the parameter, it is actually equal to the object type. In other words, dynamic is an object at the CLR level. In fact, you don’t need to remember this. We know it from the C # code generated by the compiler.

Here I use dotpeek to see the C code generated by the compiler.

By the way, do you have a C # decompiler under Mac. Ask for recommendation

So when we write overloaded methods, we can’t distinguish between object and dynamic.

void DynamicMethod(object o);
Void DynamicMethod (dynamic d); // error the compiler cannot compile: a method with the same name and parameter already exists

If dynamic is the same as object, what does it have to do with DLR?

In fact, Microsoft provides such a keyword, I think it is convenient to provide a shortcut to create dynamic types. What is really closely related to dynamic types is the namespaceSystem.DynamicType under. Main core categoriesDynamicObject,ExpandoObject,IDynamicMetaObjectProviderWe won’t talk about these three classes in this section.

The secret of DLR

First of all, let’s have a general understanding of the important function DLR added in C # 4.0, and what kind of hierarchy it is in the compiler.

Here I quote https://www.codeproject.com/Articles/42997/NET-4-0-FAQ-Part-1-The-DLR What is the meaning of a structural diagram of this article

Dynamic programming = CLR + DLR

This is enough to show the position of DLR in C #. Although the name is only one letter different from CLR, its level is actually above CLR. We know that the compiler converts the code we write into IL, and then converts it into local code through CLR, and the CPU executes the executable program. In fact, DLR does a lot of work during compilation and runtime. Finally, the C # code will be converted to CLR static language, and then the code will be converted to local code execution (such as calling function, etc.) through CLR.

Now let’s briefly introduce what DLR does during compilation.

Here, we have to use an example to illustrate. Let’s modify the above example a little


// program.cs
dynamic x = "marson shine";
string v = x.Substring(6);
Console.WriteLine(v);

To save space, I simplified and rewritten the ugly variable naming and unnecessary comments. The generated code is as follows:


 object obj1 = (object) "marson shine";
 staticCallSite1 = staticCallSite1 ?? CallSite<Func<CallSite, object, int, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "Substring", (IEnumerable<Type>) null, typeof (Example), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[2]
 {
 CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null),
 CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.Constant, (string) null)
 }));

 object obj2 = ((Func<CallSite, object, int, object>) staticCallSite1.Target)((CallSite) staticCallSite1, obj1, 6);
 staticCallSite2 = staticCallSite2 ?? CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", (IEnumerable<Type>) null, typeof (Example), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[2]
 {
 CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, (string) null),
 CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null)
 }));

 ((Action<CallSite, Type, object>) staticCallSite2.Target)((CallSite) staticCallSite2, typeof (Console), obj2);

The above two variablesstaticCallSite1,staticCallSite2Is a static variable, which plays the role of cache.

Three core concepts of DLR are involved here

  1. Expresstree (expression tree): generate and execute code with abstract syntax tree (AST) through CLR runtime. And it is also the main tool used to interact with dynamic languages (such as python, JavaScript, etc.)
  2. Callsite: when we write to call dynamically typed methods, this is a call point. These calls are static functions and can be cached, so in subsequent calls, if they are found to be of the same type, they will run faster.
  3. Binder: in addition to the call point, the system also needs to know how to call these methods, such as through call in the example Binder.InvokeMember Methods, as well as the methods called by those object types. Binders can also be cached

summary

We can summarize the DLR running process. At run time, DLR uses the expression tree, call point, binder code, and caching mechanism generated during compiling and running, so we can achieve the reuse of computation to achieve high performance. ASP.NET Four common ways of page caching

Now we know why DLR can achieve the same effect as reflection, but the performance is much higher than reflection.

Supplementary notes

I just saw that the students in the comments mentioned the performance test comparison between reflection and dynamic, and found that the reflection performance occupies an obvious advantage. In fact, from that example, it just illustrates the problem of DLR. I’ll list his test code first


const int Num = 1000 * 100;
{
 var mi = typeof(XXX).GetMethod("Go");
 var go1 = new XXX();
 for (int i = 0; i < Num; i++)
 {
 mi.Invoke(go1, null);
 }
}
{
 dynamic go1 = new XXX();
 for (int i = 0; i < Num; i++)
 {
 go1.Go();
 }
}

In this test, the reflected metadata information has been cached to the local variable MI, so when calling the method, the cached MI is actually used. In the case of no cache advantage, the performance of DLR is inferiorMethodInfo+InvokeYes.

In fact, in the summary of the article, it is also emphasized that using the caching mechanism to achieve repeated computation can improve the performance

Let’s look at an example


public void DynamicMethod(Foo f) {
 dynamic d = f;
 d.DoSomething();
}

public void ReflectionMethod(Foo f) {
 var m = typeof(Foo).GetMethod("DoSomething");
 m?.Invoke(f, null);
}

The method dosomething is just an empty method. Now let’s look at the implementation results

//Execution time
var f = new Foo();
Stopwatch sw = new Stopwatch();
int n = 10000000;
sw.Start();
for (int i = 0; i < n; i++) {
 ReflectionMethod(f);
}
sw.Stop();
Console.WriteLine("ReflectionMethod: " + sw.ElapsedMilliseconds + " ms");

sw.Restart();
for (int i = 0; i < n; i++) {
 DynamicMethod(f);
}
sw.Stop();
Console.WriteLine("DynamicMethod: " + sw.ElapsedMilliseconds + " ms");

//Output
ReflectionMethod: 1923 ms
DynamicMethod: 223 ms

Here we can clearly see the implementation time gap. In fact, the execution of DLR is represented by the following pseudo code

public void DynamicMethod(Foo f) {
 dynamic d = f;
 d.DoSomething();
}
//Here is the general code generated by DLR
static DynamicCallSite fooCallSite;
public void ReflectionMethod(Foo f) {
 object d = f;
 if(fooCallSite == null) fooCallSite = new DynamicCallSite();
 fooCallSite.Invoke("Foo",d);
}

The compiler compiles the above methodsDynamicMethodIf so, the prepared call point foocallsite will be called directly. Otherwise, as mentioned in the previous article, the call point will be generated, the binder will bind the member information, and the expression tree will be generated according to the ast expression to cache these. When calculating (calling).

Just because we know something about DLR, we naturally know how to use DLR and the keyword dynamic. For example, we now know that the C # compiler treats dynamic as an object. So we must pay attention not to be “inexplicably” boxed when using, resulting in unnecessary performance loss.

As for the application of DLR, especially the combination of dynamic language programming, to achieve the purpose of static language dynamic programming. In fact, as soon as DLR came out, there were open source components like ironpython. This is another topic, and we seldom do practical application, so we didn’t talk about it.

Supplement:

DLR mainly provides the following three functions:

1. Language implementation services provide language interoperability

2. Dynamic language runtime service provides dynamic call support

3. Public script host

Relying on these modules, you can easily do the following things

1. Add script support to your existing. Net application

2. Provide dynamic confidants for your existing language

3. Provide dynamic operation support for any object

4. Provide script language in your architecture

Parameter data:

https://www.codeproject.com/Articles/42997/NET-4-0-FAQ-Part-1-The-DLR Deep understanding of C

This article about an article to take you to understand the world of C # DLR is introduced here. For more related C # DLR content, please search previous articles of developer or continue to browse the following related articles. I hope you can support developer more in the future!