您的位置:首页 > 其它

浅谈Dynamic 关键字系列之四:dynamic为什么比反射快

2012-12-24 01:47 555 查看
staticvoidMain(string[]args)
{
dynamicstr="abcd";
Console.WriteLine(str.Length);

Console.WriteLine();
Console.WriteLine(str.Substring(1));

Console.ReadLine();
}

运行,结果如下:



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

完整代码如下:

privatestaticvoidMain(string[]args)
{
objectobj1="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),

newCSharpArgumentInfo[]{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),

newCSharpArgumentInfo[]{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)

,newCSharpArgumentInfo[]{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),newCSharpArgumentInfo[]{

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]
privatestaticclass<Main>o__SiteContainer0
{
//Fields
publicstaticCallSite<Action<CallSite,Type,object>><>p__Site1;
publicstaticCallSite<Func<CallSite,object,object>><>p__Site2;
publicstaticCallSite<Action<CallSite,Type,object>><>p__Site3;
publicstaticCallSite<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]
publicstaticclassSiteContainer
{
//Fields
publicstaticCallSite<Action<CallSite,Type,object>>p__Site1;
publicstaticCallSite<Func<CallSite,object,object>>p__Site2;
publicstaticCallSite<Action<CallSite,Type,object>>p__Site3;
publicstaticCallSite<Func<CallSite,object,int,object>>p__Site4;
}


接着看下初始化p__Site1时的方法吧:

if(SiteContainer.p__Site1==null)
{
CallSiteBindercsb=Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.ResultDiscarded,
"WriteLine",null,typeof(Program),
newCSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(

CSharpArgumentInfoFlags.IsStaticType|CSharpArgumentInfoFlags.UseCompileTimeType,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});
SiteContainer.p__Site1=CallSite<Action<CallSite,Type,object>>.Create(csb);
}



InvokeMember方法的签名:

publicstaticCallSiteBinderInvokeMember(CSharpBinderFlagsflags,stringname,

IEnumerable<Type>typeArguments,Typecontext,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)
{
CallSiteBindercsb=Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
CSharpBinderFlags.None,"Length",typeof(Program),
newCSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});

SiteContainer.p__Site2=CallSite<Func<CallSite,object,object>>.Create(csb);
}


可以看到,和p__Site1不同的是,调用的是GetMember方法。


既然有了两个CallSite<T>的对象,那么它们又是如何调用的呢??

使用CallSite<T>.Target就可以调用了。




//这是编译器生成的代码:
//SiteContainer.p__Site1.Target(SiteContainer.p__Site1,typeof(Console),
//SiteContainer.p__Site2.Target(SiteContainer.p__Site2,obj1)
//);

varpSite2Result=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对象是:

publicstaticCallSite<Func<CallSite,object,int,object>>p__Site4;


初始化:

if(SiteContainer.p__Site4==null)
{
CallSiteBindercsb=Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.None,"Substring",null,typeof(Program),
newCSharpArgumentInfo[]
{
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是常量。


调用如下:


varsubStringResult=SiteContainer.p__Site4.Target(SiteContainer.p__Site4,obj1,1);
SiteContainer.p__Site1.Target(SiteContainer.p__Site1,typeof(Console),subStringResult);

解释同上。

完整的Main函数代码如下:

staticvoidMain(string[]args)
{
objectobj1="abcd";

if(SiteContainer.p__Site1==null)
{
CallSiteBindercsb=Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.ResultDiscarded,
"WriteLine",null,typeof(Program),
newCSharpArgumentInfo[]
{
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)
{
CallSiteBindercsb=Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
CSharpBinderFlags.None,"Length",typeof(Program),
newCSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});

SiteContainer.p__Site2=CallSite<Func<CallSite,object,object>>.Create(csb);
}

if(SiteContainer.p__Site4==null)
{
CallSiteBindercsb=Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.None,"Substring",null,typeof(Program),
newCSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant|

CSharpArgumentInfoFlags.UseCompileTimeType,null)
});
SiteContainer.p__Site4=CallSite<Func<CallSite,object,int,object>>.Create(csb);
}

varlengthResult=SiteContainer.p__Site2.Target(SiteContainer.p__Site2,obj1);
SiteContainer.p__Site1.Target(SiteContainer.p__Site1,typeof(Console),lengthResult);

varsubStringResult=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是完全相同的,难道是为了实现简单??、

幸亏还有延迟初始化,否则静态字段这么多,不知道会对系统产生什么影响


运行,结果如下:




从这个例子也可以知道为什么dynamic会比反射的速度要快了。

1:if(p__Site1)==null,p__Site1==xxx,并且p__Site1是静态类中的静态字段,所以有缓存效果。

2:CallSite<T>.Target:0级缓存-基于站点历史记录专用的委托.

使用委托来调用,自然比反射又要快一些。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: