三种属性操作性能比较:PropertyInfo + Expression Tree + Deleg
在《上篇》中,我比较了三种属性操作的性能:直接操作,单纯通过PropertyInfo反射和IL Emit。本篇继续讨论这个话题,我们再引入另外两种额外的属性操作方式:Expression Tree(这和IL Emit基本一致)和通过Delegate的静态方法CreateDelegate创建相应的委托进行属性的赋值和取值。[源代码从这里下载]
一、定义测试相关的接口、类型和委托我首先定义了一个Bar类型和IFoo接口,该接口中仅仅包含一个类型和名称为Bar的可读写属性。Foo1、Foo2和Foo3均实现接口IFoo,这些接口和类型定义如下: 1: public class Bar{ } 3: { 5: } 7: { 9: } 11: { 14: class Foo3 : IFoo
16: 17: }
然后定义如下两个委托:GetPropertyValue和SetPropertyValue。如它们的名称所表示的那些,它们分别表示属性取值和赋值操作: delegate void SetPropertyValue(Bar bar);
二、通过Expression Tree的方式创建用于属性操作的委托接下来我们编写Expression Tree的方式完成属性赋值和取值的操作,它们实现在如下两个静态方法中:CreateGetPropertyValueFunc和CreateSetPropertyValueAction。下面是CreateGetPropertyValueFunc的定义,它返回的是一个Func<object.object>委托: 2: {
4: var target = Expression.Parameter(typeof(object)); 6: var getPropertyValue = Expression.Property(castTarget,property); return Expression.Lambda<Func<object>>(castPropertyvalue,target).Compile();
static Action<object> CreateSetPropertyValueAction()
7: var castPropertyValue = Expression.Convert(propertyValue,property.PropertyType); 9: return Expression.Lambda<Action<object>>(setPropertyValue,target,propertyValue).Compile(); static void TestSetPropertyValue(int times) 4: var foo2 = new Foo2();
6: var bar = new Bar();
9: var setDelegate1 = CreateSetPropertyValueDelegate(foo1); 11: var setDelegate3 = CreateSetPropertyValueDelegate(foo3); 13: var stopwatch = new Stopwatch();
15: for (int i = 0; i < times; i++) 17: property.SetValue(foo1,bar,1)">null); 19: property.SetValue(foo3,1)" id="lnum20"> 20: } 22:? 24: int i = 0; i < times; i++)
26: setAction(foo1,bar); 28: setAction(foo3,1)" id="lnum29"> 29: } 31:? 33: 34: {
36: setDelegate2(bar); 38: } 40: Console.WriteLine("{0,-15}{1,-15}{2,-15}{3,-15}",times,duration1,duration2,duration3);
void TestGetPropertyValue(new Foo1 { Bar = new Bar() }; new Foo3 { Bar = 6:?
9: var getDelegate1 = CreateGetPropertyValueDelegate(foo1); 11: var getDelegate3 = CreateGetPropertyValueDelegate(foo3); 27: var bar2 = getFunc(foo2); 35: var bar1 = getDelegate1(); 37: var bar3 = getDelegate3(); 40:? 42: } 五、执行测试程序,查看测试结果我们直接通过一个Console应用来测试,在Main()方法中编写了如下的测试程序。先三次调用TestSetPropertyValue方法测试属性赋值操作,传入表示迭代次数的参数分别为10000(一万)、100000(十万)和1000000(一百万)。然后按照相同的方式调用TestGetPropertyValue测试属性取值操作。 3: Console.WriteLine("Times",1)">"Reflection",1)">"Expression",1)">"Delegate");
5: TestSetPropertyValue(100000); 7:? 9:? 11: TestGetPropertyValue(100000); 13: } 从下面的输出结果来看,不论是属性的赋值还是取值,单纯通过PropertyInfo的方式所耗用的时间都比其它两种形式要长的多。至于其它两种(Expression Tree和通过Delegate.CreateDelegate创建委托)来说,后者又比前者有明显的优势。 2: 10000 109 2 0
4: 1000000 9872 210 37 6: 10000 80 2 0 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语句。两个方法现在的定义如下: 5: var getPropertyValue = Expression.Property(target,property);
8: static Action<IFoo,Bar> CreateSetPropertyValueAction()
10: var property = "Bar");
13: var setPropertyValue = Expression.Call(target,propertyValue); 15: } 在这种情况下,再次运行我们的测试程序,你会得到如下的输出结果。从中我们不难看出,通过上面的修改,Expression Tree形式的操作在性能上得到了一定的提升,但是和第三种依然有一定的差距。 3: 100000 982 15 3
6: 10000 79 1 0 8: 1000000 7901 178 28 晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ASP.NET MVC4 Google oAuth
- asp.net – 考虑到N2 CMS,但担心性能.这是否合理?
- asp.net-core – 如何注入对特定IHostedService实现的引用?
- asp.net-mvc – asp.net mvc应用程序中的Web浏览器托管问题
- asp.net – asp mvc http以对象作为参数获取动作
- asp.net-mvc – 不使用SignalR时,ELMAH中出现SignalR错误
- asp.net-mvc-3 – 如何模拟查询字符串
- asp.net – 实体框架和MVC应用程序中的奇怪编译错误
- asp.net – 您无权查看此目录或页面. Azure Web Api
- 当我的模型在我的ASP.NET MVC应用程序中为null时,EditorFor