C# Dynamic关键字之:dynamic为什么比反射快的详解
main方法如下:
static void main(string[] args)
{
dynamic str = "abcd";
console.writeline(str.length);
console.writeline();
console.writeline(str.substring(1));
console.readline();
}
运行,结果如下:
使用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 就可以调用了。
//这是编译器生成的代码://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是完全相同的,难道是为了实现简单?? 、幸亏还有延迟初始化,否则静态字段这么多,不知道会对系统产生什么影响 运行,
结果如下:
从这个例子也可以知道为什么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; }