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

在《上篇》中,我比较了三种属性操作的性能:直接操作,单纯通过PropertyInfo反射和IL
Emit。本篇继续讨论这个话题,我们再引入另外两种额外的属性操作方式:Expression Tree(这和IL
Emit基本一致)和通过Delegate的静态方法CreateDelegate创建相应的委托进行属性的赋值和取值。[源代码从这里下载]

目录
一、定义测试相关的接口、类型和委托
二、通过Expression Tree的方式创建用于属性操作的委托
三、编写属性赋值操作测试方法
四、编写属性取值操作测试方法
五、执行测试程序,查看测试结果
六、如果在Expression Tree中避免类型转换呢?

一、定义测试相关的接口、类型和委托

我首先定义了一个Bar类型和IFoo接口,该接口中仅仅包含一个类型和名称为Bar的可读写属性。Foo1、Foo2和Foo3均实现接口IFoo,这些接口和类型定义如下:

   1: public class Bar{ }
   2: public interface IFoo
   3: {
   4:     Bar Bar { get; set; }
   5: }
   6: public class Foo1 : IFoo
   7: {
   8:     public Bar Bar { get; set; }
   9: }
  10: public class Foo2 : IFoo
  11: {
  12:     public Bar Bar { get; set; }
  13: }
  14: public class Foo3 : IFoo
  15: {
  16:     public Bar Bar { get; set; }
  17: }

然后定义如下两个委托:GetPropertyValue和SetPropertyValue。如它们的名称所表示的那些,它们分别表示属性取值和赋值操作:

   1: public delegate Bar GetPropertyValue();
   2: public delegate void SetPropertyValue(Bar bar);

二、通过Expression Tree的方式创建用于属性操作的委托

接下来我们编写Expression
Tree的方式完成属性赋值和取值的操作,它们实现在如下两个静态方法中:CreateGetPropertyValueFunc和CreateSetPropertyValueAction。下面是CreateGetPropertyValueFunc的定义,它返回的是一个Func<object.object>委托:

   1: public static Func<object, object> CreateGetPropertyValueFunc()
   2: {
   3:     var property            = typeof(IFoo).GetProperty("Bar");
   4:     var target              = Expression.Parameter(typeof(object));
   5:     var castTarget          = Expression.Convert(target, typeof(IFoo));
   6:     var getPropertyValue    = Expression.Property(castTarget, property);
   7:     var castPropertyvalue   = Expression.Convert(getPropertyValue, typeof(object));
   8:     return Expression.Lambda<Func<object, object>>(castPropertyvalue , target).Compile();
   9: }

下面是CreateSetPropertyValueAction方法,返回一个Action<object.object>委托:

   1: public static Action<object, object> CreateSetPropertyValueAction()
   2: {
   3:     var property            = typeof(IFoo).GetProperty("Bar");
   4:     var target              = Expression.Parameter(typeof(object));
   5:     var propertyValue       = Expression.Parameter(typeof(object));
   6:     var castTarget          = Expression.Convert(target, typeof(IFoo));
   7:     var castPropertyValue   = Expression.Convert(propertyValue, property.PropertyType);
   8:     var setPropertyValue    = Expression.Call(castTarget, property.GetSetMethod(), castPropertyValue);
   9:     return Expression.Lambda<Action<object, object>>(setPropertyValue, target, propertyValue).Compile();
  10: }

三、编写属性赋值操作测试方法

接下来我们编写程序测试三种不同的属性赋值操作分别具有怎样的性能,所有的测试代码定义在如下TestSetPropertyValue静态方法中。该方法参数表示进行属性赋值操作迭代的次数,每次迭代分别对Foo1、Foo2和Foo3三个对象的Bar属性进行赋值。最后打印出三种赋值操作分别的耗时,时间单位为毫秒。

   1: public static void TestSetPropertyValue(int times)
   2: {
   3:     var foo1            = new Foo1();
   4:     var foo2            = new Foo2();
   5:     var foo3            = new Foo3();
   6:     var bar             = new Bar();
   7:     var property        = typeof(IFoo).GetProperty("Bar");
   8:     var setAction       = CreateSetPropertyValueAction();
   9:     var setDelegate1    = CreateSetPropertyValueDelegate(foo1);
  10:     var setDelegate2    = CreateSetPropertyValueDelegate(foo2);
  11:     var setDelegate3    = CreateSetPropertyValueDelegate(foo3);
  12:  
  13:     var stopwatch = new Stopwatch();
  14:     stopwatch.Start();
  15:     for (int i = 0; i < times; i++)
  16:     {
  17:         property.SetValue(foo1, bar,null);
  18:         property.SetValue(foo2, bar, null);
  19:         property.SetValue(foo3, bar, null);
  20:     }
  21:     var duration1 = stopwatch.ElapsedMilliseconds;
  22:  
  23:     stopwatch.Restart();
  24:     for (int i = 0; i < times; i++)
  25:     {
  26:         setAction(foo1, bar);
  27:         setAction(foo2, bar);
  28:         setAction(foo3, bar);
  29:     }
  30:     var duration2 = stopwatch.ElapsedMilliseconds;
  31:  
  32:     stopwatch.Restart();
  33:     for (int i = 0; i < times; i++)
  34:     {
  35:         setDelegate1(bar);
  36:         setDelegate2(bar);
  37:         setDelegate3(bar);
  38:     }
  39:     var duration3 = stopwatch.ElapsedMilliseconds;            
  40:     Console.WriteLine("{0, -15}{1,-15}{2,-15}{3,-15}", times, duration1, duration2, duration3);
  41: }

四、编写属性取值操作测试方法

属性取值操作的测试方法TestGetPropertyValue与TestSetPropertyValue结构一样。先实例化三个IFoo对象(类型分别分Foo1、Foo2和Foo3),并初始化了它们的Bar属性。然后按照三种不同的方式获取该属性值,并打印出它们各自的耗时。

   1: public static void TestGetPropertyValue(int times)
   2: {
   3:     var foo1            = new Foo1 { Bar = new Bar() };
   4:     var foo2            = new Foo2 { Bar = new Bar() };
   5:     var foo3            = new Foo3 { Bar = new Bar() };
   6:  
   7:     var property        = typeof(IFoo).GetProperty("Bar");
   8:     var getFunc         = CreateGetPropertyValueFunc();
   9:     var getDelegate1    = CreateGetPropertyValueDelegate(foo1);
  10:     var getDelegate2    = CreateGetPropertyValueDelegate(foo2);
  11:     var getDelegate3    = CreateGetPropertyValueDelegate(foo3);
  12:  
  13:     var stopwatch = new Stopwatch();
  14:     stopwatch.Start();
  15:     for (int i = 0; i < times; i++)
  16:     {
  17:         var bar1 = property.GetValue(foo1, null);
  18:         var bar2 = property.GetValue(foo2, null);
  19:         var bar3 = property.GetValue(foo3, null);
  20:     }
  21:     var duration1 = stopwatch.ElapsedMilliseconds;
  22:  
  23:     stopwatch.Restart();
  24:     for (int i = 0; i < times; i++)
  25:     {
  26:         var bar1 = getFunc(foo1);
  27:         var bar2 = getFunc(foo2);
  28:         var bar3 = getFunc(foo3);
  29:     }
  30:     var duration2 = stopwatch.ElapsedMilliseconds;
  31:  
  32:     stopwatch.Restart();
  33:     for (int i = 0; i < times; i++)
  34:     {
  35:         var bar1 = getDelegate1();
  36:         var bar2 = getDelegate2();
  37:         var bar3 = getDelegate3();
  38:     }
  39:     var duration3 = stopwatch.ElapsedMilliseconds;
  40:  
  41:     Console.WriteLine("{0, -15}{1,-15}{2,-15}{3,-15}", times, duration1, duration2, duration3);
  42: }

五、执行测试程序,查看测试结果

我们直接通过一个Console应用来测试,在Main()方法中编写了如下的测试程序。先三次调用TestSetPropertyValue方法测试属性赋值操作,传入表示迭代次数的参数分别为10000(一万)、100000(十万)和1000000(一百万)。然后按照相同的方式调用TestGetPropertyValue测试属性取值操作。

   1: static void Main()
   2: {
   3:     Console.WriteLine("{0, -15}{1,-15}{2,-15}{3,-15}", "Times", "Reflection", "Expression", "Delegate");
   4:     TestSetPropertyValue(10000);
   5:     TestSetPropertyValue(100000);
   6:     TestSetPropertyValue(1000000);
   7:  
   8:     Console.WriteLine();
   9:  
  10:     TestGetPropertyValue(10000);
  11:     TestGetPropertyValue(100000);
  12:     TestGetPropertyValue(1000000);
  13: }

从下面的输出结果来看,不论是属性的赋值还是取值,单纯通过PropertyInfo的方式所耗用的时间都比其它两种形式要长的多。至于其它两种(Expression
Tree和通过Delegate.CreateDelegate创建委托)来说,后者又比前者有明显的优势。

   1: Times          Reflection     Expression     Delegate
   2: 10000          109            2              0
   3: 100000         992            21             3
   4: 1000000        9872           210            37
   5:  
   6: 10000          80             2              0
   7: 100000         800            23             2
   8: 1000000        8007           239            28

六、如果在Expression Tree中避免类型转换呢?

当我们调用Delegate的静态方法CreateDelegate是,需要指定具体的委托类型。对于属性的操作来说,属性类型需要与指定的委托类型相匹配,所以这就避免了类型转化这个步骤。但是对于Expression

Tree的属性操作来说,由于返回的类型是Func<object,object>和Action<object,object>,需要对目标对象和属性值进行两次类型转换。如果将类型转换这个步骤从Expression
Tree中移掉,两者的性能是否一致呢?

我们不妨来试试看。现在我们修改CreateGetPropertyValueFunc和CreateSetPropertyValueAction这两个静态方法,让它们直接返回Func<IFoo,Bar>和Action<IFoo,
Bar>,并去掉Expression.Convert语句。两个方法现在的定义如下:

   1: public static Func<IFoo, Bar> CreateGetPropertyValueFunc()
   2: {
   3:     var property            = typeof(IFoo).GetProperty("Bar");
   4:     var target              = Expression.Parameter(typeof(IFoo));
   5:     var getPropertyValue    = Expression.Property(target, property);
   6:     return Expression.Lambda<Func<IFoo, Bar>>(getPropertyValue, target).Compile();
   7: }
   8: public static Action<IFoo, Bar> CreateSetPropertyValueAction()
   9: {
  10:     var property            = typeof(IFoo).GetProperty("Bar");
  11:     var target              = Expression.Parameter(typeof(IFoo));
  12:     var propertyValue       = Expression.Parameter(typeof(Bar));
  13:     var setPropertyValue    = Expression.Call(target, property.GetSetMethod(), propertyValue);
  14:     return Expression.Lambda<Action<IFoo, Bar>>(setPropertyValue, target, propertyValue).Compile();
  15: }

在这种情况下,再次运行我们的测试程序,你会得到如下的输出结果。从中我们不难看出,通过上面的修改,Expression Tree形式的操作在性能上得到了一定的提升,但是和第三种依然有一定的差距。

   1: Times          Reflection     Expression     Delegate
   2: 10000          107            1              0
   3: 100000         982            15             3
   4: 1000000        9802           157            37
   5:  
   6: 10000          79             1              0
   7: 100000         789            18             2
   8: 1000000        7901           178            28

晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo

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

关于Expression Tree和IL Emit的所谓的"性能差别"

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-31 22:30:34

三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate的相关文章

一起谈.NET技术,三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate

在<上篇>中,我比较了三种属性操作的性能:直接操作,单纯通过PropertyInfo反射和IL Emit.本篇继续讨论这个话题,我们再引入另外两种额外的属性操作方式:Expression Tree(这和IL Emit基本一致)和通过Delegate的静态方法CreateDelegate创建相应的委托进行属性的赋值和取值.[源代码从这里下载] 目录 一.定义测试相关的接口.类型和委托 二.通过Expression Tree的方式创建用于属性操作的委托 三.编写属性赋值操作测试方法 四.编写属性取

Delphi2010中DataSnap高级技术(7)—TDSServerClass中Lifecycle生命周期三种属性说明

Lifecycle 三种属性: Session.Invocation.Server 这三种属性都用在什么情况,有什么要注意的事项,Delphi2010中罕有说明. 如果乱用这三种属性,你的服务程序有可能崩溃,数据混乱,内存占用大,效率低等问题! 下面我对这三种属性的使用环境逐一介绍: 1. Session 说明:这是delphi2010中默认属性,也是delphi推荐设置.Session会为每个来自客户端的链接,建立一个线程来实例化.实例化是什么概念呢?就是这个线程把所有你将要用到的类.函数等等

关于Expression Tree和IL Emit的所谓的&quot;性能差别&quot;

昨天写了<三种属性操作性能比较>,有个网友写信问我一个问题:从性能上看,Expression Tree和IL Emit孰优孰劣?虽然我在回信中作了简单的回答,但不知道这个网友是否懂我的意思.反正今天呆在家里也没事儿,干脆再就这个话题再写一篇文章. 目录: 一.Expression Tree和IL Emit并不存在所谓的性能差异 二.属性赋值操作的两种写法 三.属性取值操作的两种写法 四.两种写法对应的IL   一.Expression Tree和IL Emit并不存在所谓的性能差异 Expre

一起谈.NET技术,关于Expression Tree和IL Emit的所谓的&amp;quot;性能差别&amp;quot;

昨天写了<三种属性操作性能比较>,有个网友写信问我一个问题:从性能上看,Expression Tree和IL Emit孰优孰劣?虽然我在回信中作了简单的回答,但不知道这个网友是否懂我的意思.反正今天呆在家里也没事儿,干脆再就这个话题再写一篇文章. 目录: 一.Expression Tree和IL Emit并不存在所谓的性能差异 二.属性赋值操作的两种写法 三.属性取值操作的两种写法 四.两种写法对应的IL 一.Expression Tree和IL Emit并不存在所谓的性能差异 Express

关于Expression Tree和IL Emit的所谓的&amp;quot;性能差别&amp;quot“.NET研究”;

昨天写了<三种属性操作性能比较>,有个网友写信问我一个问题:从性能上看,Expression Tree和IL Emit孰优孰劣?虽然我在回信中作了简单的回答,但不知道这个网友是否懂我的意思.反正今天呆在家里也没事儿,干脆再就这个话题再写一篇文章. 目录: 一.Expression Tree和IL Emit并不存在所谓的性能差异 二.属性赋值操作的两种写法 三.属性取值操作的两种写法 四.两种写法对应的IL 一.Expression Tree和IL Emit并不存在所谓的性能差异 Express

JS面向对象(3)之Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法_javascript技巧

1.Object类 在JS中,Object是所有类的基类,使用Object类来创建自定义对象时,可以无需定义构造函数(constructor,prototype,hasOwnProperty(property)) var per = new Object(); per.name = 'zhangsan'; per.age = ; alert(per.name + per.age); 我们想在程序中得到一个对象变量,只要能存储大量数据即可,这个时候,我们可以考虑使用Object类.Object类避

《用Python写网络爬虫》——2.2 三种网页抓取方法

2.2 三种网页抓取方法 现在我们已经了解了该网页的结构,下面将要介绍三种抓取其中数据的方法.首先是正则表达式,然后是流行的BeautifulSoup模块,最后是强大的lxml模块. 2.2.1 正则表达式 如果你对正则表达式还不熟悉,或是需要一些提示时,可以查阅https://docs.python.org/2/howto/regex.html 获得完整介绍. 当我们使用正则表达式抓取面积数据时,首先需要尝试匹配 元素中的内容,如下所示. >>> import re >>&

ASP、JSP、PHP 三种技术比较

js|比较|js 目前,最常用的三种动态网页语言有ASP(Active Server Pages),JSP(JavaServer Pages),PHP (Hypertext Preprocessor). 简 介 ASP全名Active Server Pages,是一个WEB服务器端的开发环境,利用它可以产生和执行动态的.互动的.高性能的WEB服务应用程序.ASP采用脚本语言VBScript(Java script)作为自己的开发语言. PHP是一种跨平台的服务器端的嵌入式脚本语言.它大量地借用C

动态网页制作:ASP、JSP、PHP三种技术比较

js|比较|动态|网页 目前,最常用的三种动态网页语言有ASP(Active Server Pages),JSP(JavaServer Pages),PHP (Hypertext Preprocessor). 简 介 ASP全名Active Server Pages,是一个WEB服务器端的开发环境,利用它可以产生和执行动态的.互动的.高性能的WEB服务应用程序.ASP采用脚本语言VBScript(Java script)作为自己的开发语言. PHP是一种跨平台的服务器端的嵌入式脚本语言.它大量地