zoukankan      html  css  js  c++  java
  • 浅谈Dynamic 关键字系列之四:dynamic为什么比反射快

    Main方法如下:

    static void Main(string[] args)
    {
        dynamic str = "abcd";
        Console.WriteLine(str.Length);
    
        Console.WriteLine();
        Console.WriteLine(str.Substring(1));
    
    
        Console.ReadLine();
    }
    

    运行,结果如下:

    clip_image002

     

    使用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返回dynamic
    2:dynamic.Length;
    3:Console.WriteLine(dynamic); str.Substring 返回dynamic
    4: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,在这里是Program
    5: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 就可以调用了。
     
    image 
     
    //这是编译器生成的代码:
    //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是完全相同的,难道是为了实现简单??
    幸亏还有延迟初始化,否则静态字段这么多,不知道会对系统产生什么影响
     
    运行,结果如下:
    clip_image002[4]
     
     
    从这个例子也可以知道为什么dynamic会比反射的速度要快了。
    1:if(p__Site1)==null,p__Site1==xxx,并且p__Site1是静态类中的静态字段,所以有缓存效果。
    2:CallSite<T>.Target: 0 级缓存 - 基于站点历史记录专用的委托.
    使用委托来调用,自然比反射又要快一些。
     
  • 相关阅读:
    简单工厂模式&工厂方法模式&抽象工厂模式的区别及优缺点及使用场景
    JDK1.8的新特性
    在Button样式中添加EventSetter,理解路由事件
    关于C#低版本升级高版本时,项目中引用Microsoft.Office.Interop.Word,程序提示不存在类型或命名空间名office.
    无法安装或运行此应用程序。该应用程序要求首先在"全局程序集缓存(GAC)"中安装程序集
    C#winform跨窗体传值和调用事件的办法
    C#线程处理:七、线程实列
    C#线程处理:六、线程同步(三)
    C#线程处理:五、线程同步(二)
    C#线程处理:四、线程同步
  • 原文地址:https://www.cnblogs.com/LoveJenny/p/2100416.html
Copyright © 2011-2022 走看看