C# Dynamic关键字之:dynamic为什么比反射快的详解
发布时间:2021-06-24 04:22:35
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 级缓存 - 基于站点历史记录专用的委托.
使用委托来调用,自然比反射又要快一些。


猜你喜欢
- 本文实例讲述了Android亮屏和熄屏控制实现方法。分享给大家供大家参考,具体如下:一、概述我们的Android应用程序很多需要和亮屏和熄屏
- 1、普通用户与系统管理员用户的权限要有严格的区分。如果一个普通用户在使用查询语句中嵌入另一个Drop Table语句,那么是否允许
- 网上关于如何切换,其实说的很明确,本文主要通过profile进行快速切换已实现在不同场合下,用不同的打包方式。jar到war修改步骤pom文
- 思路:首先进入登录界面,输入账号和密码后登陆到主界面,在主界面通过点击按钮发送一条强制下线的广播,广播接收者收到广播后重新进入登陆界面。新建
- 组合模式及其在JDK源码中的运用 前言组合和聚合什么是组合模式示例透明组合模式透明组合模式的缺陷安全组合模式 组合模式角色组合模式在JDK源
- Java语言是SUN(Stanford University Network,斯坦福大学网络公司)公司1995年推出的一门高级编程语言,起初
- 前言这几天琢磨着开发个个人作品的时候,发现原来Unity3D官方没有提供圆锥体的创建功能,就自己做了个编辑器扩展。鉴于之前搜索Mesh编程的
- 本文实例讲述了Android判断设备网络连接状态及判断连接方式的方法。分享给大家供大家参考,具体如下:在Android开发过程中,对于一个需
- 一、RabbitMQ介绍RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发
- 1. 认识ZoneZone像一个沙盒,是我们代码执行的一个环境。我们的main函数默认就运行在Root Zone当中。子Zone的构造有点像
- 昨天在与对端系统调接口的时候,对端系统对我们传过去的json串老是处理不了,后来查原因是应为我们传过去的json串里有json对象数组,因为
- 序列化一般应用与以下场景之中:1.永久性保存对象,把对象通过序列化字节流保存到本地文件中;2.通过序列化在网络中传输对象3.通过序列化在进程
- 近日在工作中需要根据设备的HardwareID来获取设备的驱动程序信息,比如驱动程序版本等。经过摸索,得到了两种不同的解决办法,两种办法各有
- FileWriter/FileReader介绍:FileWriter 类从 OutputStreamWriter 类继承而来。该类按字符向流
- C#延时函数使用在线程中如果需要延时,尽量不要使用Sleep()函数,这样会导致时间片切到别的线程中。使用如下函数:
- Java 判断字符串中是否包含中文的实例详解 Java判断一个字符串是否有中文是利用Unicode编码来判断,因为中
- 1、maven打包Spring Boot项目的pom.xml文件中默认使用spring-boot-maven-plugin插件进行打包:&l
- 本文实例讲述了Java基于外观模式实现美食天下食谱功能。分享给大家供大家参考,具体如下:一、模式定义外观模式,是软件工程师常用的一种软件设计
- 1.java过滤器过滤允许整个项目跨域访问,可通过filter来进行过虑:public class SimpleCORSFilter imp
- 本文实例为大家分享了使用的是iTextSharp添加PDF水印的具体代码,供大家参考,具体内容如下需要iTextSharp.dll, 下载地