一起谈.NET技术,关于Expression Tree和IL Emit的所谓的quot;性能差别quot;
昨天写了《三种属性操作性能比较》,有个网友写信问我一个问题:从性能上看,Expression Tree和IL Emit孰优孰劣?虽然我在回信中作了简单的回答,但不知道这个网友是否懂我的意思。反正今天呆在家里也没事儿,干脆再就这个话题再写一篇文章。
目录:
一、Expression Tree和IL Emit并不存在所谓的性能差异
二、属性赋值操作的两种写法
三、属性取值操作的两种写法
四、两种写法对应的IL
一、Expression Tree和IL Emit并不存在所谓的性能差异
Expression Tree和IL Emit的性能孰优孰劣,这本是个“不是问题的问题”。因为两者之间并不存在本质的区别,所以也谈不上性能的优劣问题。举个例子来说,我们知道.NET Framework 2.0,3.0和3.5使用的是相同的CLR。但是C# 3.0、3.5在2.0的基础上推出了很多语言层面的特性,比如自动实现属性:
{
public Bar Bar{get;set;}
public Foo()
{
this.Bar = new Bar();
}
}
我们也可以按照下面“传统”的方式来写上面这段代码,谁都知道这两种写法在本质上是完全一样的。就上面的程序来说,在编译的时候C#编译器会将其转化成下一种形式,什么自动实现属性、匿名属性、扩展方法,都是浮云——语法糖而已。
{
private Bar _bar;
public Bar Bar
{
get{return _bar;}
set{_bar = value;}
}
public Foo()
{
_bar = new Bar();
}
}
Expression Tree和IL Emit之间的关系与这些“语法糖”类似。编译后的Expression Tree就是IL代码;而IL Emit让我们可以用高级语言的编程方式来控制中间语言(IL)程序。由于最终的东西都是一样的,谈不上谁比谁好的问题。编译Expression Tree实现了向IL的转换,如果你通过IL Emit写的IL能够比Expression Tree自动转换的好,那么你的程序性能就好,否则性能就差。但是我们不能说Expression Tree和IL Emit在性能上孰优孰劣。
二、属性赋值操作的两种写法
我们说明Expression Tree和IL Emit之间不存在性能的差异,我们不妨写个例子。简单起见,我们还是采用前面谈到过的属性赋值和取值的操作为例。假设有如下一个接口IFoo,包含一个类型和名称均为Bar的可读写的属性。
{
Bar{get;set;}
}
public class Bar{}
现在我们通过Expression Tree和IL Emit两种方式编写一个静态方法对IFoo对象的Bar属性进行赋值。简单起见,我们甚至将静态方法的参数类型直接指定为IFoo和Bar,从而省去了类型转换操作。下面是通过Expression Tree进行属性赋值的方法:SetPropertyValueViaExpression。
{
var property = typeof(IFoo).GetProperty("Bar");
var target = Expression.Parameter(typeof(IFoo));
var propertyValue = Expression.Parameter(typeof(Bar));
var setPropertyValue = Expression.Call(target, property.GetSetMethod(), propertyValue);
var setAction= Expression.Lambda<Action<IFoo, Bar>>(setPropertyValue, target, propertyValue).Compile();
setAction(foo, bar);
}
而下面的SetPropertyValueViaEmit则通过IL Emit的方式完成了一样的工作:
{
var property = typeof(IFoo).GetProperty("Bar");
DynamicMethod method = new DynamicMethod("SetValue", null, new Type[] { typeof(IFoo), typeof(Bar) });
ILGenerator ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.EmitCall(OpCodes.Callvirt, property.GetSetMethod(), null);
ilGenerator.Emit(OpCodes.Ret);
method.DefineParameter(1, ParameterAttributes.In, "obj");
method.DefineParameter(2, ParameterAttributes.In, "value");
var setAction = (Action<IFoo, Bar>)method.CreateDelegate(typeof(Action<IFoo, Bar>));
setAction(foo, bar);
}
三、属性取值操作的两种写法
接下来,我们来编写用于进行属性取值操作的方法。下面的SetPropertyValueViaExpression方法是基于Expression Tree的。
{
var property = typeof(IFoo).GetProperty("Bar");
var target = Expression.Parameter(typeof(IFoo));
var getPropertyValue = Expression.Property(target, property);
var getFunc = Expression.Lambda<Func<IFoo, Bar>>(getPropertyValue, target).Compile();
return getFunc(foo);
}
下面则是基于IL Emit的版本:
{
var property = typeof(IFoo).GetProperty("Bar");
DynamicMethod method = new DynamicMethod("GetValue", typeof(Bar), new Type[] { typeof(IFoo) });
ILGenerator ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.EmitCall(OpCodes.Callvirt, property.GetGetMethod(), null);
ilGenerator.Emit(OpCodes.Ret);
method.DefineParameter(1, ParameterAttributes.In, "target");
var getFunc = (Func<IFoo, Bar>)method.CreateDelegate(typeof(Func<IFoo, Bar>));
return getFunc(foo);
}
四、看看两种写法对应的IL
我们说过,经过编译的Expression Tree就是一段IL代码,而IL Emit则直接反映了IL的执行流程。要判断两者在性能方面孰优孰劣,我们只需要看看Expression Tree最终被转换成怎样的IL。我们现在的做法是动态生成一个程序集,将Expression Tree部分定义到一个方法之中。虽然IL Emit已经是真实底反映了底层的IL代码,但是为了我们的比较更加直观,我们也将IL Emit的部分也写入相应的方法。
为此我们在一个Console应用中的Main方法编写了如下的代码:动态创建了名称为Artech.EmitVsExpression的程序集,其中定义了同名的模块。一个唯一的类型Program定义其中,其中定义了四个静态方法:GetPropertyValueViaExpression、SetPropertyValueViaExpression、GetPropertyValueViaEmit和GetPropertyValueViaEmit。而方法体部分则是上面Expression Tree和IL Emit定义的内容。最后这个程序集被保存为一个同名的.dll文件。
{
var property = typeof(IFoo).GetProperty("Bar");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Artech.EmitVsExpression"), AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("Artech.EmitVsExpression", "Artech.EmitVsExpression.dll");
var typeBuilder = moduleBuilder.DefineType("Program");
//GetPropertyValueViaExpression
var methodBuilder = typeBuilder.DefineMethod("GetPropertyValueViaExpression", MethodAttributes.Static | MethodAttributes.Public, typeof(Bar), new Type[] { typeof(IFoo) });
var target = Expression.Parameter(typeof(IFoo));
var getPropertyValue = Expression.Property(target, property);
Expression.Lambda<Func<IFoo, Bar>>(getPropertyValue, target).CompileToMethod(methodBuilder);
//SetPropertyValueViaExpression
methodBuilder = typeBuilder.DefineMethod("SetPropertyValueViaExpression", MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(IFoo), typeof(Bar) });
target = Expression.Parameter(typeof(IFoo));
var propertyValue = Expression.Parameter(typeof(Bar));
var setPropertyValue = Expression.Call(target, property.GetSetMethod(), propertyValue);
Expression.Lambda<Action<IFoo, Bar>>(setPropertyValue, target, propertyValue).CompileToMethod(methodBuilder);
//GetPropertyValueViaEmit
methodBuilder = typeBuilder.DefineMethod("GetPropertyValueViaEmit", MethodAttributes.Static| MethodAttributes.Public, typeof(Bar), new Type[] { typeof(IFoo) });
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.EmitCall(OpCodes.Callvirt, property.GetGetMethod(), null);
ilGenerator.Emit(OpCodes.Ret);
//SetPropertyValueViaEmit
methodBuilder = typeBuilder.DefineMethod("SetPropertyValueViaEmit", MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(IFoo), typeof(Bar) });
ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.EmitCall(OpCodes.Callvirt, property.GetSetMethod(), null);
ilGenerator.Emit(OpCodes.Ret);
typeBuilder.CreateType();
assemblyBuilder.Save("Artech.EmitVsExpression.dll");
}
现在我们通过IL Disassembler打开这个.dll文件,看看四个静态方法的IL代码。下面是用于用于获取属性值的GetPropertyValueViaExpression和GetPropertyValueViaEmit方法,我们可以看出它们具有完全一致的方式体。
GetPropertyValueViaExpression(class [EmitVsExpressionTree]IFoo A_0) cil managed
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: callvirt instance class [EmitVsExpressionTree]Bar [EmitVsExpressionTree]IFoo::get_Bar()
IL_0006: ret
} // end of method Program::GetPropertyValueViaExpression
.method public static class [EmitVsExpressionTree]Bar
GetPropertyValueViaEmit(class [EmitVsExpressionTree]IFoo A_0) cil managed
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: callvirt instance class [EmitVsExpressionTree]Bar [EmitVsExpressionTree]IFoo::get_Bar()
IL_0006: ret
} // end of method Program::GetPropertyValueViaEmit
下面是用于对属性进行赋值的两个静态方法:SetPropertyValueViaExpression和SetPropertyValueViaEmit,毫无疑问它们之间也没有差异。到现在,你还在怀疑两种之间在性能上孰优孰劣吗?
class [EmitVsExpressionTree]Bar A_1) cil managed
{
// Code size 8 (0x8)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: callvirt instance void [EmitVsExpressionTree]IFoo::set_Bar(class [EmitVsExpressionTree]Bar)
IL_0007: ret
} // end of method Program::SetPropertyValueViaExpression
.method public static void SetPropertyValueViaEmit(class [EmitVsExpressionTree]IFoo A_0,
class [EmitVsExpressionTree]Bar A_1) cil managed
{
// Code size 8 (0x8)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: callvirt instance void [EmitVsExpressionTree]IFoo::set_Bar(class [EmitVsExpressionTree]Bar)
IL_0007: ret
} // end of method Program::SetPropertyValueViaEmit
既然在IL上它们没有差别,那么它们就是两对等效的方法。如果你通过Reflector来打开我们生成的.dll,你会清晰地看到这真的是两对完全一致的方法。
{
// Methods
public static Bar GetPropertyValueViaEmit(IFoo foo1)
{
return foo1.Bar;
}
public static Bar GetPropertyValueViaExpression(IFoo foo1)
{
return foo1.Bar;
}
public static void SetPropertyValueViaEmit(IFoo foo1, Bar bar1)
{
foo1.Bar = bar1;
}
public static void SetPropertyValueViaExpression(IFoo foo1, Bar bar1)
{
foo1.Bar = bar1;
}
}
转载于:https://www.cnblogs.com/waw/archive/2011/09/01/2162908.html
一起谈.NET技术,关于Expression Tree和IL Emit的所谓的quot;性能差别quot;相关推荐
- 一起谈.NET技术,Expression Blend中文教程 - 开篇
随着计算机软件开发分工细节化,微软对已有的产品线进行了调整,在保持原有经典开发工具Visual Studio基础上,又推出了一套新的设计开发工具系列,Expression Studio. Expres ...
- 一起谈.NET技术,.Net Discovery系列之-深入理解平台机制与性能影响 (中)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马-JIT.有关JIT的机制分析 ● 机制分析以C#为例,在C#代码运行前 ...
- 三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate
在<上篇>中,我比较了三种属性操作的性能:直接操作,单纯通过PropertyInfo反射和IL Emit.本篇继续讨论这个话题,我们再引入另外两种额外的属性操作方式:Expression ...
- Expression Tree 上手指南 (二)
上回我们说到Expression Tree是一种表示编程语言中"表达式"概念的树状数据结构,并且学习了从Lambda表达式自动生成表达式树的C#语法.那么它到底有什么用呢?其实上一 ...
- 一起谈.NET技术,一句代码实现批量数据绑定[下篇]
<上篇>主要介绍如何通过DataBinder实现批量的数据绑定,以及如何解决常见的数据绑定问题,比如数据的格式化.接下来,我们主要来谈谈DataBinder的设计,看看它是如何做到将作为数 ...
- 反射,Expression Tree,IL Emit 属性操作对比
.net的反射(Reflection) 是.Net中获取运行时类型信息的一种方法,通过反射编码的方式可以获得 程序集,模块,类型,元数据等信息. 反射的优点在于微软提供的API调用简单,使用方便: 表 ...
- 浅谈虚拟化技术下的云安全如何处置
浅谈虚拟化技术下的云安全如何处置 近年来,云计算是目前非常热门的一个研究领域,其实它并不是一种全新的技术,而是许多技术的融合体,包括分布式计算.动态和拓展等各种各样的技术算法,而虚拟化技术是云计算里最 ...
- 技术创业需胆识 谈IT技术人员的创业 (http://www.cnblogs.com/dudu/archive/2007/05/27/761740.html)...
技术创业需胆识 谈IT技术人员的创业 作者:李立辉 我自己介绍一下:本人96年西点毕业,学的是无线通信,柳传志是我的校友,后来分配到北京巨龙做交换机,98年去了深圳中兴,当时看到中兴日益强大,和华为的 ...
- 浅谈.NET技术公司的实习生培养
浅谈.NET技术公司的实习生培养 背景 近几年.NET开发者市场的越发不景气,一毕业就选择.NET技术的开发者更是少之又少.一方面是公司效益的日益提高,一方面却是招聘优秀人才的速度总是赶不上公司发展的 ...
最新文章
- rsync问题-connection refused(111)、Unknown module
- 如何关闭window10自动更新
- VS2008中VC 项目 文件目录的管理
- HDU - 1863-畅通工程(最小生成树)
- springBoot集成Mybatis-Generator
- 2022电工杯:5G 网络环境下应急物资配送问题(优化)
- 友善之臂Mini2440NORflash bootloader烧写详述---亲测可用
- Android基础--ListView的刷新
- 自动化测试的7个步骤
- 关于position: sticky的简单用法
- OSChina 周二乱弹 —— 好好告别啊!不要舌吻!
- Android 获取手机状态栏高度
- 面试关于jvm、dvm和art虚拟机区别
- Xilinx 7系列FPGA收发器架构之接收器(RX)(十一)
- Shader实现高光反射
- 世界杯“诈骗杯”?小心这些就对了
- LINUX 字体装在哪个文件夹里
- matlab整流桥和电感怎么连接,整流桥四脚接法实物图手把手教学
- JAVA生成二维码链接(扫描二维码后进行指定链接跳转)
- 乐行天下激光雷达SDK介绍
热门文章
- Ubuntu18.04及以上设备安装CUDA,CUDNN,Anaconda亲测方法
- C++ Primer Plus学习(十四)——友元、异常和其他
- stl之list双向链表容器应用基础
- python闭包技巧_案例详析:Python闭包与nonlocal关键字
- 计算机网络应用基础_2020年天津市成人高考 高起专 计算机基础题型总结
- vba有下拉框的模糊查找_巧用数据验证制作模糊匹配的下拉列表
- linux sys伪用户作用,linux用户管理详解
- java设置时间_怎么用java设置系统时间?
- linux备份数据库软件有哪些内容,Linux网络备份MySQL数据库的应用方法
- java 深入io_Java高级编程-IO操作深入