C#中表达式树和反射来访问对象属性的性能比较浅析

今天在工作上遇到这么个需求:需要获取对象上所有属性的值,但并事先并不知道对象的类型。 我的第一反应就是使用反射,但是这个操作会进行多次,大量的反射肯定会有性能影响。虽然对我这个项目无关紧要,但我还是选择了另外一种解决方案:构建表达式树,再生成委托,然后将委托缓存在字典里。代码如下:

首先构建表达式树(类似这种形式:'(a) => a.xx'),并生成委托:

 

 代码如下 复制代码

private static Delegate BuildDynamicGetPropertyValueDelegate(PropertyInfo property)
{
    var instanceExpression = Expression.Parameter(property.ReflectedType, "instance");
    var memberExpression = Expression.Property(instanceExpression, property);

    var lambdaExpression = Expression.Lambda(memberExpression, instanceExpression);
    return lambdaExpression.Compile();
}

 

接着,当需要获取属性的值时,先在字典里查看是否有已经生成好的委托,有的话取出委托执行获取属性值。没有则构建表达式树生成委托,并放入字典中:

 

 代码如下 复制代码

private static Dictionary<PropertyInfo, Delegate> delegateCache = new Dictionary<PropertyInfo, Delegate>();

public static object GetPropertyValueUseExpression<TObject>(TObject obj, PropertyInfo property)
{        
    if (delegateCache.ContainsKey(property))
    {
        var func = (Func<TObject, object>)delegateCache[property];
        return func(obj);
    }

    var getValueDelegate = BuildDynamicGetPropertyValueDelegate(property);
    delegateCache[property] = getValueDelegate;
    return ((Func<TObject, object>)getValueDelegate)(obj);
}

 

就这么简单,完成之后,我想测试一下表达式树版本和反射版本的性能差距如何,于是我又简单实现反射版本作为测试对比:

 

 代码如下 复制代码
public static object GetPropertyValueUseReflection<TObject>(TObject obj, PropertyInfo propertyInfo)
{
    return propertyInfo.GetValue(obj);
}

 

接下来是两者的测试代码:

 

 代码如下 复制代码

class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int Capacity { get; set; }
}

.....

int repeatTimes = 10000;
PropertyInfo property = typeof(Car).GetProperty("Make");
Car car = new Car();

Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < repeatTimes; i++)
{
    GetPropertyValueUseExpression(car, property);
}
stopwatch.Stop();
Console.WriteLine("Repeated {0}, Cache in Dictionary expression used time: {1} ms", repeatTimes, stopwatch.ElapsedTicks);

stopwatch.Reset();
stopwatch.Start();
for (int i = 0; i < repeatTimes; i++)
{
    GetPropertyValueUseReflection(car, property);
}
stopwatch.Stop();
Console.WriteLine("Repeated {0}, reflection used time: {1} ms", repeatTimes, stopwatch.ElapsedTicks);

 

在我的预想之中是这样的:表达式树版本在调用次数很少的情况下会慢于反射版本,随着次数增多,表达式树版本的优势会越来越明显。

但是测试结果却出乎我的意料!!!

 


 

 


 

 


 

在调用次数为十万、百万、千万次的情况下,两者所用的时间差不多,而且反射版本居然还要快一些。这可让我郁闷不已。

郁闷之后,我就在想是不是因为字典的原因导致两者性能差不多,就添加了以下测试代码:

 

 代码如下 复制代码
stopwatch.Reset();
stopwatch.Start();
var func = (Func<Car, object>)BuildDynamicGetPropertyValueDelegate(property);
for (int i = 0; i < repeatTimes; i++)
{
    func(car);
}
stopwatch.Stop();
Console.WriteLine("Repeated {0}, Immediate call expression used time: {1} ticks", repeatTimes, stopwatch.ElapsedTicks);

 

 

这部分测试代码,在构建表达式树生成委托之后,直接调用,去除了字典的影响。测试结果如下:

 


 

果不其然,去除字典之后速度快了10倍。

看来在我这种情况下使用字典缓存委托的效果并不是太好。不知道是否有更好的方法来缓存委托。

时间: 2024-08-03 07:53:09

C#中表达式树和反射来访问对象属性的性能比较浅析的相关文章

浅析JavaScript访问对象属性和方法及区别_javascript技巧

属性是一个变量,用来表示一个对象的特征,如颜色.大小.重量等:方法是一个函数,用来表示对象的操作,如奔跑.呼吸.跳跃等. 在JavaScript中通常使用"."运算符来存取对象的属性的值.或者使用[]作为一个关联数组来存取对象的属性. 对象的属性和方法统称为对象的成员. 访问对象的属性 在JavaScript中,可以使用" . "和" [ ] "来访问对象的属性. 1. 使用" . "来访问对象属性 语法:     objec

javascript-求前端大神解释js动态访问对象属性的问题

问题描述 求前端大神解释js动态访问对象属性的问题 在js我有一个这样的需求:对象temp里面有name1name2,name3 属性这样访问属性:temp.(""name""+1)temp.(""name""+2)temp.(""name""+3)为什么不行?但是在groovy里是可以的 解决方案 这个市解释器不一样吧你要是使用 temp['name'+1]肯定是好使的.所以编写的js

javascript面向对象之访问对象属性的两种方式分析_javascript技巧

本文实例分析了javascript面向对象之访问对象属性的两种方式.分享给大家供大家参考.具体如下: javascript面向对象的访问对象属性的两种方式.如下代码所示: 复制代码 代码如下: <script language="javascript" type="text/javascript"> function Person(){}; var p1 = new Person(); p1.name="王美人"; document.

利用Linq快速访问对象属性

今天看<Linq in Action>时突然萌生了一个想法使用Expression和Linq来快速访问对象的属性.如果我们把一个对象放到数组中对其进行查询,然后使用select将需要的属性投影出来就可以达到快速访问的目的.虽然比直接访问要慢很多,但是比反射方式快1个数量级还是非常不错的. 假设有实体类定义如下: public class User ...{ public string Name ...{ get; set; } public int Age ...{ get; set; } }

浅谈JS使用[ ]来访问对象属性_javascript技巧

对象的属性由两种固定的方法来访问:"."记法和"[ ]"方括号记法: 使用"."号记法访问标准的对象属性,使用"[ ]"方括号记法访问由页面定义的对象属性.如下 document.forms["myformname"].elements["myinput"].value 这里,forms 是 document 的一个标准属性,而表单名 myformname 则是由页面所定义的.同时,el

JavaScript对象属性检查、增加、删除、访问操作实例_javascript技巧

检查属性 var mouse = { "name": "betta", "age": 3, "varieties": "milaoshu" } mouse.hasOwnProperty("name"); // true mouse.hasOwnProperty("sex"); //false 增加属性 定义个对象 dog,然后赋予各种特性,再赋予 color特性,最后

谈表达式树的缓存(1):引言

表达式树(Expression Tree)是.NET 3.5中引入的一种表达方式.表达式树的运用十分广泛,可以直 观地表现出各种"数据",甚至"逻辑"和"行为".再者,表达式树是强类型的,因此合理地使用这个 新特性可以让代码编写变得优雅,方便.一个最简单而常见的例子便是,某些朋友目前就已经喜欢使用表 达式树来代替传统的ByXxx方法,尤其是在访问一些直接支持表达式树的数据源时(例如IEnumerable或 LINQ to SQL).如下: pub

el表达式-EL表达式类对象访问私有属性

问题描述 EL表达式类对象访问私有属性 在EL表达式中,为什么用对象就能得到类中属性的值,属性是私有的 解决方案 EL表达式获取对象属性的原理是这样的: 以表达式${user.name}为例 EL表达式会根据name去User类里寻找这个name的get方法,此时会自动把name首字母大写并加上get前缀,一旦找到与之匹配的方法,El表达式就会认为这就是要访问的属性,并返回属性的值. 所以,想要通过EL表达式获取对象属性的值,那么这个属性就必须有与之对应的get方法. 解决方案二: 其实你要了解

Lambda表达式表达式树

在C#3.0中,继匿名方法之后出现了Lambda 表达式,使表达更为简洁.快捷.Lambda 表达式使用Lambda 运算符 "=>"来定义,语法如下: (参数列表) => {方法体} Lambda 运算符的左边是输入参数,定义Lambda表达式的接收参数列表,右边包含表达式或语句块,表示将表达式的值或语句块返回的值传给左边的参数列表. Lambda 表达式是一个匿名函数,可以包含表达式和语句,并且可用于创建委托或表达式目录树类型. 以下部分来自MSDN:点击打开链接 La