浅谈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 级缓存 - 基于站点历史记录专用的委托.
使用委托来调用,自然比反射又要快一些。
 





本文转自LoveJenny博客园博客,原文链接:http://www.cnblogs.com/LoveJenny/archive/2011/07/07/2100416.html,如需转载请自行联系原作者
目录
相关文章
|
API 图形学
【Unity细节】RigidBody中Dynamic和Kinematic的区别
【Unity细节】RigidBody中Dynamic和Kinematic的区别
140 0
【C++】类的构造函数的基本介绍(A basic introduction to constructors of classes)
【C++】类的构造函数的基本介绍(A basic introduction to constructors of classes)
|
存储 iOS开发 开发者
追根问底:Objective-C关联属性原理分析
Objective-C是一种动态性很强的语言,所谓动态能力,也可以理解为运行时能力。对于Objective-C开发者来说,动态性所带来的编程便利无处不在。例如通过Category类别来扩展已有类的功能。可以使已有类拥有新的方法和属性。
168 0
【Groovy】MOP 元对象协议与元编程 ( Expando 动态类 | 创建动态类 | 为动态类增加字段和方法 )
【Groovy】MOP 元对象协议与元编程 ( Expando 动态类 | 创建动态类 | 为动态类增加字段和方法 )
185 0
【Groovy】MOP 元对象协议与元编程 ( Expando 动态类 | 创建动态类 | 为动态类增加字段和方法 )