欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# Dynamic关键字之:dynamic为什么比反射快的详解

程序员文章站 2023-12-17 12:34:22
main方法如下:复制代码 代码如下:static void main(string[] args){    dynamic str = "a...

main方法如下:

复制代码 代码如下:

static void main(string[] args)
{
    dynamic str = "abcd";
    console.writeline(str.length);

    console.writeline();
    console.writeline(str.substring(1));


    console.readline();
}


运行,结果如下:

C# Dynamic关键字之:dynamic为什么比反射快的详解

使用reflactor 反编译下,可以看到:

完整代码如下:

复制代码 代码如下:

private static void main(string[] args)
{
      object obj1 = "abcd";
      if (program.<main>o__sitecontainer0.<>p__site1 == null)
      {
            program.<main>o__sitecontainer0.<>p__site1 = callsite<action<callsite, type, object>>.create(binder.invokemember(csharpbinderflags.resultdiscarded, "writeline", null, typeof(program), new csharpargumentinfo[] { csharpargumentinfo.create(csharpargumentinfoflags.isstatictype | csharpargumentinfoflags.usecompiletimetype, null), csharpargumentinfo.create(csharpargumentinfoflags.none, null) }));
      }
      if (program.<main>o__sitecontainer0.<>p__site2 == null)
      {
            program.<main>o__sitecontainer0.<>p__site2 = callsite<func<callsite, object, object>>.create(binder.getmember(csharpbinderflags.none, "length", typeof(program), new csharpargumentinfo[] { csharpargumentinfo.create(csharpargumentinfoflags.none, null) }));
      }
      program.<main>o__sitecontainer0.<>p__site1.target(program.<main>o__sitecontainer0.<>p__site1, typeof(console), program.<main>o__sitecontainer0.<>p__site2.target(program.<main>o__sitecontainer0.<>p__site2, obj1));
      console.writeline();
      if (program.<main>o__sitecontainer0.<>p__site3 == null)
      {
            program.<main>o__sitecontainer0.<>p__site3 = callsite<action<callsite, type, object>>.create(binder.invokemember(csharpbinderflags.resultdiscarded, "writeline", null, typeof(program), new csharpargumentinfo[] { csharpargumentinfo.create(csharpargumentinfoflags.isstatictype | csharpargumentinfoflags.usecompiletimetype, null), csharpargumentinfo.create(csharpargumentinfoflags.none, null) }));
      }
      if (program.<main>o__sitecontainer0.<>p__site4 == null)
      {
            program.<main>o__sitecontainer0.<>p__site4 = callsite<func<callsite, object, int, object>>.create(binder.invokemember(csharpbinderflags.none, "substring", null, typeof(program), new csharpargumentinfo[] { csharpargumentinfo.create(csharpargumentinfoflags.none, null), csharpargumentinfo.create(csharpargumentinfoflags.constant | csharpargumentinfoflags.usecompiletimetype, null) }));
      }
      program.<main>o__sitecontainer0.<>p__site3.target(program.<main>o__sitecontainer0.<>p__site3, typeof(console), program.<main>o__sitecontainer0.<>p__site4.target(program.<main>o__sitecontainer0.<>p__site4, obj1, 1));
      console.readline();
}

首先编译器会自动生成一个静态类:如下:
复制代码 代码如下:

[compilergenerated]
private static class <main>o__sitecontainer0
{
      // fields
      public static callsite<action<callsite, type, object>> <>p__site1;
      public static callsite<func<callsite, object, object>> <>p__site2;
      public static callsite<action<callsite, type, object>> <>p__site3;
      public static callsite<func<callsite, object, int, object>> <>p__site4;
}

为什么这里有四个callsite<t>的对象呢?在我们的代码中

console.writeline(str.length);
console.writeline();
console.writeline(str.substring(1));
一共使用了四次dynamic对象。1:console.writeline(dynamic); str.length返回dynamic2:dynamic.length;3:console.writeline(dynamic); str.substring 返回dynamic4:dynamic.substring(1); 1,2,3,4,分别对应上面的<>p_site1,2,3,4;

因为1,3 都是无返回值的,所以是action, 2,4都有返回值,所以是func. 看上面的代码可能还不清楚,让我们手动的生成下代码吧:新建类sitecontainer 来取代编译器自动生成的类。

复制代码 代码如下:

[compilergenerated]
public static class sitecontainer
{
  // fields
  public static callsite<action<callsite, type, object>> p__site1;
  public static callsite<func<callsite, object, object>> p__site2;
  public static callsite<action<callsite, type, object>> p__site3;
  public static callsite<func<callsite, object, int, object>> p__site4;
}

接着看下初始化p__site1时的方法吧:
复制代码 代码如下:

if (sitecontainer.p__site1 == null)
{
    callsitebinder csb= microsoft.csharp.runtimebinder.binder.invokemember(
        csharpbinderflags.resultdiscarded,
        "writeline", null, typeof(program),
        new csharpargumentinfo[]
        {
            csharpargumentinfo.create(csharpargumentinfoflags.isstatictype | csharpargumentinfoflags.usecompiletimetype,null),
            csharpargumentinfo.create(csharpargumentinfoflags.none,null)
        });
    sitecontainer.p__site1 = callsite<action<callsite, type, object>>.create(csb);
}

invokemember方法的签名:public static callsitebinder invokemember(csharpbinderflags flags, string name, ienumerable<type> typearguments, type context, ienumerable<csharpargumentinfo> argumentinfo); 1:在这里csharpbinderflags传递的是resultdiscarded,代表结果被丢弃,   所以可以绑定到一个void的返回方法中。2:name传递的是”writeline”,要调用的方法的名称。3:typearguments.类型参数的列表,传递null。4:context: 用于指示此操作的使用位置的 system.type,在这里是program5:argumentinfo:参数信息,   接着看看p__site2如何初始化的吧:
复制代码 代码如下:

if (sitecontainer.p__site2 == null)
{
    callsitebinder csb = microsoft.csharp.runtimebinder.binder.getmember(
        csharpbinderflags.none, "length", typeof(program),
        new csharpargumentinfo[]
        {
            csharpargumentinfo.create(csharpargumentinfoflags.none, null)
        });

    sitecontainer.p__site2 = callsite<func<callsite, object, object>>.create(csb);
}

可以看到,和p__site1不同的是,调用的是getmember方法。
 
既然有了两个callsite<t>的对象,那么它们又是如何调用的呢??
使用callsite<t>.target 就可以调用了。
 
C# Dynamic关键字之:dynamic为什么比反射快的详解
 
//这是编译器生成的代码://sitecontainer.p__site1.target(sitecontainer.p__site1, typeof(console), // sitecontainer.p__site2.target(sitecontainer.p__site2, obj1) //); var psite2result = sitecontainer.p__site2.target(sitecontainer.p__site2, obj1); sitecontainer.p__site1.target(sitecontainer.p__site1, typeof(console), psite2result);

 
看看如何调用的吧:
因为sitecontainer.p__site2,是调用length属性
首先调用p__site2的target方法,执行p__site2,对象是obj1.
dlr 就会调用obj1.length,并返回结果,所以psite2result=4;
接着调用p__site1的target,来调用console类的writeline方法,参数是psite2result.所以输出4.
 
最后来看下dynamic是如何调用substring方法的:

substring方法对应的是p__site4,因为substring方法传递了个参数1,并且有返回值,所以

p__site4对象是:

public static callsite<func<callsite, object, int, object>> p__site4;
初始化:

复制代码 代码如下:

if (sitecontainer.p__site4 == null)
{
    callsitebinder csb = microsoft.csharp.runtimebinder.binder.invokemember(
        csharpbinderflags.none, "substring", null, typeof(program),
        new csharpargumentinfo[]
        {
            csharpargumentinfo.create(csharpargumentinfoflags.none, null),
            csharpargumentinfo.create(csharpargumentinfoflags.constant
            | csharpargumentinfoflags.usecompiletimetype, null)                       
        });
    sitecontainer.p__site4 = callsite<func<callsite, object, int, object>>.create(csb);
}

基本和上面的p__site1类似,只是参数信息是:csharpargumentinfoflags.constant \

因为调用了substring(1).在编译的时候会传递1进去,而1是常量。 调用如下:

复制代码 代码如下:

var substringresult = sitecontainer.p__site4.target(sitecontainer.p__site4, obj1, 1);
sitecontainer.p__site1.target(sitecontainer.p__site1, typeof(console), substringresult);

解释同上。

完整的main函数代码如下:

复制代码 代码如下:

static void main(string[] args)
{
    object obj1 = "abcd";

    if (sitecontainer.p__site1 == null)
    {
        callsitebinder csb = microsoft.csharp.runtimebinder.binder.invokemember(
            csharpbinderflags.resultdiscarded,
            "writeline", null, typeof(program),
            new csharpargumentinfo[]
            {
                csharpargumentinfo.create(csharpargumentinfoflags.isstatictype | csharpargumentinfoflags.usecompiletimetype,null),
                csharpargumentinfo.create(csharpargumentinfoflags.none,null)
            });
        sitecontainer.p__site1 = callsite<action<callsite, type, object>>.create(csb);
    }

    if (sitecontainer.p__site2 == null)
    {
        callsitebinder csb = microsoft.csharp.runtimebinder.binder.getmember(
            csharpbinderflags.none, "length", typeof(program),
            new csharpargumentinfo[]
            {
                csharpargumentinfo.create(csharpargumentinfoflags.none, null)
            });

        sitecontainer.p__site2 = callsite<func<callsite, object, object>>.create(csb);
    }

    if (sitecontainer.p__site4 == null)
    {
        callsitebinder csb = microsoft.csharp.runtimebinder.binder.invokemember(
            csharpbinderflags.none, "substring", null, typeof(program),
            new csharpargumentinfo[]
            {
                csharpargumentinfo.create(csharpargumentinfoflags.none, null),
                csharpargumentinfo.create(csharpargumentinfoflags.constant | csharpargumentinfoflags.usecompiletimetype, null)    
            });
        sitecontainer.p__site4 = callsite<func<callsite, object, int, object>>.create(csb);
    }

    var lengthresult = sitecontainer.p__site2.target(sitecontainer.p__site2, obj1);
    sitecontainer.p__site1.target(sitecontainer.p__site1, typeof(console), lengthresult);


    var substringresult = sitecontainer.p__site4.target(sitecontainer.p__site4, obj1, 1);
    sitecontainer.p__site1.target(sitecontainer.p__site1, typeof(console), substringresult);

    console.readline();
}


在这里,我没有使用p__site3,因为p__site3和p__site1相同,不过为什么微软会生成4个callsite<t>对象,因为1 和3是完全相同的,难道是为了实现简单?? 、幸亏还有延迟初始化,否则静态字段这么多,不知道会对系统产生什么影响 运行,
结果如下:

 C# Dynamic关键字之:dynamic为什么比反射快的详解
从这个例子也可以知道为什么dynamic会比反射的速度要快了。
1:if(p__site1)==null,p__site1==xxx,并且p__site1是静态类中的静态字段,所以有缓存效果。
2:callsite<t>.target: 0 级缓存 - 基于站点历史记录专用的委托.
使用委托来调用,自然比反射又要快一些。.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "courier new", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

上一篇:

下一篇: