在《上篇》中,我比较了三种属性操作的性能:直接操作,单纯通过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的所谓的"性能差别"

转载于:https://www.cnblogs.com/artech/archive/2011/03/26/Propertyaccesstest.html

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

  1. php ismethod,结合php类三种属性说明is_callable和method_exists简单区别

    class Man{ public function one(){ echo 'public '; } protected function two(){ echo 'protected'; } st ...

  2. VELOCITY三种属性加载方式

    一.velocity默认的加载方式(文件加载方式) package com.velocity.test; import java.io.StringWriter; import java.util.P ...

  3. 制作CSS绚烂效果的三种属性

    animation(动画).transition(过渡).transform(变形) https://www.cnblogs.com/shenfangfang/p/5713564.html 转载于:h ...

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

    昨天写了<三种属性操作性能比较>,有个网友写信问我一个问题:从性能上看,Expression Tree和IL Emit孰优孰劣?虽然我在回信中作了简单的回答,但不知道这个网友是否懂我的意思 ...

  5. 表达式树(EXPRESSION TREE)

    表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression类.创建表达式树具体有两个优势: 1.对表达 ...

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

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

  7. HTMLDOM中三种元素节点、属性节点、文本节点的测试案例

    HTML dom中常用的三种节点分别是元素节点.属性节点.文本节点. 具体指的内容可参考下图: 以下为测试用例: <!DOCTYPE html> <html><head& ...

  8. android获取自定义属性,android 自定义控件中获取属性的三种方式(转)

    第一种方法,直接设置属性值,通过attrs.getAttributeResourceValue拿到这个属性值. (1)在xml文件中设置属性值 android:layout_width="f ...

  9. mybatis count返回null_Mybatis属性示例-Properties的三种配置方式

    1.项目结构 开发工具:IDEA+Maven 项目结构 2.配置 2.1.Maven配置 Maven配置文件pom.xml,增加Mysql8数据库连接类库.Mybatis类库.Log4j2类库.Bas ...

最新文章

  1. bing浏览器_微软推出全新 Edge 浏览器,这 3 大特色亮点 Chrome 都没有
  2. 搭建LoadRunner中的场景(三)场景的执行计划
  3. NetBeans 时事通讯(刊号 # 103 - May 18, 2010)
  4. Android开发之GridView的使用(解读谷歌官方API)
  5. 跳出小程序 video组件 卡顿、黑屏、全屏等坑
  6. 以二维振动为例展示使用matlab画圆形三维图的偷懒方法
  7. java字节流分为_Java文件流可分为字节流和字符流。
  8. html 修改按回退键的url,location.hash保存页面状态的技巧
  9. fetch ajax cros,由 Fetch 跨域 看 CORS
  10. 【报告分享】数据资产化之路----数据资产的估值与行业实践.pdf
  11. mybatis中prefix,suffix,prefixOverrides,suffixOverrides用法解释
  12. 中石油职称计算机试题,中石油职称计算机水平考试复习题库22-职称计算机考试其它试卷与试题.pdf...
  13. dll依赖查看工具-depends
  14. git config配置
  15. 关于“Fatal signal 11 (SIGSEGV) at 0x00000004 (code=1), thread 7592 (xample.hellojni)”android NDK错误排查
  16. 炮轰三国服务器维护,炮轰三国上红色要多少个精华 | 手游网游页游攻略大全
  17. 大脑的无限存储与记忆传输
  18. python基础编程题(一)
  19. JavaGUI编程 -- Swing之Icon、ImageIcon标签获取当前类同一级文件路径的资源
  20. 数据库和数据库软件的安装

热门文章

  1. 强化学习组队学习task01——基础
  2. 计算机视觉基础:图像处理Task01-图像插值算法
  3. 基于keras实现多标签分类(multi-label classification)
  4. javascript range 转为 html,javascript Range对象跨浏览器常用操作
  5. android开发复制文本,如何在Android应用中以编程方式复制文本?
  6. python ttk.notebook_python – 无法在ttk.Notebook中看到所有选项卡
  7. 一汽奔腾b7o价位_全新奔腾B70正式上市,前脸被吐槽酷似某豪华品牌
  8. python数据分析实例_Python数据分析及可视化实例之爬虫源码(05)
  9. ROS学习:智能车室外光电组仿真
  10. php对象依赖注入作用,php面向对象依赖注入理解及代码举例分析解释