昨天写了《三种属性操作性能比较》,有个网友写信问我一个问题:从性能上看,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 class Foo
{
public Bar Bar{get;set;}
public Foo()
{
this.Bar = new Bar();
}
}

  我们也可以按照下面“传统”的方式来写上面这段代码,谁都知道这两种写法在本质上是完全一样的。就上面的程序来说,在编译的时候C#编译器会将其转化成下一种形式,什么自动实现属性、匿名属性、扩展方法,都是浮云——语法糖而已。

public class Foo
{
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的可读写的属性。

public interface IFoo
{
Bar{get;set;}
}
public class Bar{}

  现在我们通过Expression Tree和IL Emit两种方式编写一个静态方法对IFoo对象的Bar属性进行赋值。简单起见,我们甚至将静态方法的参数类型直接指定为IFoo和Bar,从而省去了类型转换操作。下面是通过Expression Tree进行属性赋值的方法:SetPropertyValueViaExpression。

public static void SetPropertyValueViaExpression(IFoo foo, Bar bar)
{
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的方式完成了一样的工作:

public static void SetPropertyValueViaEmit(IFoo foo, Bar bar)
{
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的。

public static Bar GetPropertyValueViaExpression(IFoo foo)
{
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的版本:

public static Bar GetPropertyValueViaEmit(IFoo foo)
{
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文件。

static void Main()
{
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方法,我们可以看出它们具有完全一致的方式体。

.method public static class [EmitVsExpressionTree]Bar
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,毫无疑问它们之间也没有差异。到现在,你还在怀疑两种之间在性能上孰优孰劣吗?

method public static void SetPropertyValueViaExpression(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::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,你会清晰地看到这真的是两对完全一致的方法。

internal class Program
{
// 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;相关推荐

  1. 一起谈.NET技术,Expression Blend中文教程 - 开篇

    随着计算机软件开发分工细节化,微软对已有的产品线进行了调整,在保持原有经典开发工具Visual Studio基础上,又推出了一套新的设计开发工具系列,Expression Studio. Expres ...

  2. 一起谈.NET技术,.Net Discovery系列之-深入理解平台机制与性能影响 (中)

    上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马-JIT.有关JIT的机制分析 ● 机制分析以C#为例,在C#代码运行前 ...

  3. 三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate

    在<上篇>中,我比较了三种属性操作的性能:直接操作,单纯通过PropertyInfo反射和IL Emit.本篇继续讨论这个话题,我们再引入另外两种额外的属性操作方式:Expression ...

  4. Expression Tree 上手指南 (二)

    上回我们说到Expression Tree是一种表示编程语言中"表达式"概念的树状数据结构,并且学习了从Lambda表达式自动生成表达式树的C#语法.那么它到底有什么用呢?其实上一 ...

  5. 一起谈.NET技术,一句代码实现批量数据绑定[下篇]

    <上篇>主要介绍如何通过DataBinder实现批量的数据绑定,以及如何解决常见的数据绑定问题,比如数据的格式化.接下来,我们主要来谈谈DataBinder的设计,看看它是如何做到将作为数 ...

  6. 反射,Expression Tree,IL Emit 属性操作对比

    .net的反射(Reflection) 是.Net中获取运行时类型信息的一种方法,通过反射编码的方式可以获得 程序集,模块,类型,元数据等信息. 反射的优点在于微软提供的API调用简单,使用方便: 表 ...

  7. 浅谈虚拟化技术下的云安全如何处置

    浅谈虚拟化技术下的云安全如何处置 近年来,云计算是目前非常热门的一个研究领域,其实它并不是一种全新的技术,而是许多技术的融合体,包括分布式计算.动态和拓展等各种各样的技术算法,而虚拟化技术是云计算里最 ...

  8. 技术创业需胆识 谈IT技术人员的创业 (http://www.cnblogs.com/dudu/archive/2007/05/27/761740.html)...

    技术创业需胆识 谈IT技术人员的创业 作者:李立辉 我自己介绍一下:本人96年西点毕业,学的是无线通信,柳传志是我的校友,后来分配到北京巨龙做交换机,98年去了深圳中兴,当时看到中兴日益强大,和华为的 ...

  9. 浅谈.NET技术公司的实习生培养

    浅谈.NET技术公司的实习生培养 背景 近几年.NET开发者市场的越发不景气,一毕业就选择.NET技术的开发者更是少之又少.一方面是公司效益的日益提高,一方面却是招聘优秀人才的速度总是赶不上公司发展的 ...

最新文章

  1. rsync问题-connection refused(111)、Unknown module
  2. 如何关闭window10自动更新
  3. VS2008中VC 项目 文件目录的管理
  4. HDU - 1863-畅通工程(最小生成树)
  5. springBoot集成Mybatis-Generator
  6. 2022电工杯:5G 网络环境下应急物资配送问题(优化)
  7. 友善之臂Mini2440NORflash bootloader烧写详述---亲测可用
  8. Android基础--ListView的刷新
  9. 自动化测试的7个步骤
  10. 关于position: sticky的简单用法
  11. OSChina 周二乱弹 —— 好好告别啊!不要舌吻!
  12. Android 获取手机状态栏高度
  13. 面试关于jvm、dvm和art虚拟机区别
  14. Xilinx 7系列FPGA收发器架构之接收器(RX)(十一)
  15. Shader实现高光反射
  16. 世界杯“诈骗杯”?小心这些就对了
  17. LINUX 字体装在哪个文件夹里
  18. matlab整流桥和电感怎么连接,整流桥四脚接法实物图手把手教学
  19. JAVA生成二维码链接(扫描二维码后进行指定链接跳转)
  20. 乐行天下激光雷达SDK介绍

热门文章

  1. Ubuntu18.04及以上设备安装CUDA,CUDNN,Anaconda亲测方法
  2. C++ Primer Plus学习(十四)——友元、异常和其他
  3. stl之list双向链表容器应用基础
  4. python闭包技巧_案例详析:Python闭包与nonlocal关键字
  5. 计算机网络应用基础_2020年天津市成人高考 高起专 计算机基础题型总结
  6. vba有下拉框的模糊查找_巧用数据验证制作模糊匹配的下拉列表
  7. linux sys伪用户作用,linux用户管理详解
  8. java设置时间_怎么用java设置系统时间?
  9. linux备份数据库软件有哪些内容,Linux网络备份MySQL数据库的应用方法
  10. java 深入io_Java高级编程-IO操作深入