IT教程 ·

一文带你领会 C# DLR 的天下

Linux tcpdump 命令详解与示例

一文带你相识 C# DLR 的天下

在良久之前,我写了一片文章dynamic连系匿名范例 匿名对象传参,内里我以为DLR内部是用反射完成的。由于那时候是心中想固然的以为只需反射能够在运转时剖析对象的成员信息并挪用成员要领。厥后也是由于其他的事一向都没有回过头来把这一节学问给补上,正所谓亡羊补牢,让我们如今来大抵相识一下DLR吧。

DLR 全称是 Dynamic Language Runtime(动态言语运转时)。这很轻易让我们想到同在C#中另有一个叫 CLR 的东西,它叫 Common Language Runtime。那这两者有什么关联呢?这个后续再说

DLR 是 C#4.0 新引进来的观点,其主要目标就是为了动态绑定与交互

C#关键字 dynamic

DLR 起首定义了一个中心范例观点,即动态范例。即在运转时肯定的范例,动态范例的成员信息、要领等都只在运转时举行绑定。与CLR的静态范例相反,静态范例都是在C#编译时期经过历程一系列的划定规矩婚配到末了的绑定。

将这类动态举行绑定的历程它有点相似反射,但其内部却和反射有很大的差别。这个轻微会谈到。

由动态范例组成的对象叫动态对象。

DLR平常有以下特性:

  1. 把CLR的一切范例悉数隐式转成dynamic。如dynamic x = GetReturnAnyCLRType()
  2. 一样,dynamic险些也能够转换成CLR范例。
  3. 一切含有动态范例的表达式都是在运转期举行动态盘算的。

DLR生长到如今,我们险些都运用了动态范例关键字 dynamic以及另有援用DLR的类库 Dapper等。

在我们不想建立新的静态类做DTO映照时,我们第一时间会想到动态范例。也经常性的将dynamic作为参数运用。

这时候我们就要注重一些 dynamic 不为大多人知的一些细节了。

不是只需含有 dynamic 的表达式都是动态的。

什么意义呢,且看这段代码dynamic x = "marson shine";。这句代码很简单,就是将字符串赋值给动态范例 x。

人人不要以为这就是动态范例了哦,实在不是,假如单单只是这一句的话,C#编译器在编译时期是会把变量 x 转变成静态范例 object 的,等价于object x = "marson shine";。大概有些人会惊奇,为何C#编译器末了会生成object范例的代码。这就是接下来我们要注重的。

dynamic 于 object 的不可告人的关联

实在假如你是以 dynamic 范例为参数,那末实际上它就是即是 object 范例的。换句话说,dynamic在CLR级别就是object。实在这点不必记,我们从编译器生成的C#代码就晓得了。

这里我用的是dotpeek检察编译器生成的c#代码。

这里趁便想问下列位,有无mac下c#反编译的东西。求引荐

所以我们在写重载要领时,是不能以 object 和 dynamic 来辨别的。

void DynamicMethod(object o);
void DynamicMethod(dynamic d);  // error 编译器没法经过历程编译:已存在同名同形参的要领

假如说 dynamic 与 object 一样,那末它与 DLR 又有什么关联呢?

实在微软供应这么一个关键字,我以为是轻易供应建立动态范例的快捷方式。而真正于动态范例密切相关的是定名空间System.Dynamic下的范例。主要中心类DynamicObject,ExpandoObject,IDynamicMetaObjectProvider ,关于这三个类我们这节先不谈。

DLR探秘

起首我们来大抵相识C#4.0到场的主要功用 DLR,在编译器中处于什么条理构造。

在这里我援用 https://www.codeproject.com/Articles/42997/NET-4-0-FAQ-Part-1-The-DLR 这片文章的一副构造图的意义

一文带你领会 C# DLR 的天下 IT教程 第1张

动态编程 = CLR + DLR

这足以申明 DLR 在C#中的位置,虽然名字与CLR只需一个字母之差,然则它所处的条理实际上是在CLR之上的。我们晓得编译器将我们写的代码转换成IL,然后经过CLR转换成当地代码交由CPU实行可实行程序。那末实际上,DLR 是在编译时期和运转期做了大批事情。末了照样会将C#代码转换成CLR静态言语,然后再经过 CLR 将代码转换成当地代码实行(如挪用函数等)。

如今我们来扼要引见一下DLR在编译时期做了什么。

到这里就不得不以例子来做申清楚明了,我们就上面的例子略加革新一下:

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

为了节约篇幅,我简化并改写了丢脸的变量定名以及不必要的解释。生成的代码以下:

            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);

上文的两个变量staticCallSite1,staticCallSite2 是静态变量,起到缓存的作用。

这里触及到了DLR中心三个观点

  1. ExpressTree(表达式树):经过历程CLR运转时用笼统语法树(AST)生成代码并实行。而且它也是用来与动态言语交互的主要东西(如Python,JavaScript 等)
  2. CallSite(挪用点):当我们写的挪用动态范例的要领,这就是一个挪用点。这些挪用都是静态函数,是能够缓存下来的,所以在后续的挪用,假如发现是雷同范例的挪用,就会更快的运转。
  3. Binder(绑定器):除了挪用点以外,体系还需要晓得这些要领怎样挪用,就比方例子中的经过历程挪用Binder.InvokeMember要领,以及是那些对象范例挪用的要领等信息。绑定器也是能够缓存的

总结

DLR运转历程我们总结起来就是,在运转时DLR应用编译运转时期生成的表达式树挪用点绑定器代码,以及缓存机制,我们就能够做到盘算的重用来到达高机能。在很早前从老赵的表达式树缓存系列文章也指出了,应用表达式树缓存机能最接近直接挪用(固然不包括IL编程)。

如今我们就晓得了为何DLR能干出与反射雷同的结果,然则机能要远比反射要高的缘由了。

假装优雅地实现定时缓存装饰器

参与评论