博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用泛型委托,构筑最快的通用属性访问器
阅读量:6869 次
发布时间:2019-06-26

本文共 7643 字,大约阅读时间需要 25 分钟。

最近做一个父类的属性向子类的属性赋值的小程序,用了下AutoMapper组件,感觉不错,想探究下它的原理,自己动手做一个例子试试看。实现这个功能,第一反应使用反射遍历对象的属性然后获取父类对象的属性值,接着设置给子类对象同名的属性。但一想到反射的效率,就又打算才用另外的方式来实现。

搜索了下资料,发现了Artech写的《三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate》 ,文中的测试结果说明,使用委托是最快的方式,但是原文进做了原理性说明,代码不通用,于是参照原文的方法,改写成泛型方法了:

首先,定义一个获取属性值和设置属性值的泛型委托:

public delegate T GetPropertyValue
(); public delegate void SetPropertyValue
(T Value);

然后,写2个构造该委托实例的方法:

private static ConcurrentDictionary
myDelegateCache = new ConcurrentDictionary
(); public static GetPropertyValue
CreateGetPropertyValueDelegate
(TSource obj, string propertyName) { string key = string.Format("Delegate-GetProperty-{0}-{1}", typeof(TSource).FullName, propertyName); GetPropertyValue
result = (GetPropertyValue
)myDelegateCache.GetOrAdd( key, newkey => { return Delegate.CreateDelegate(typeof(GetPropertyValue
), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod()); } ); return result; } public static SetPropertyValue
CreateSetPropertyValueDelegate
(TSource obj, string propertyName) { string key = string.Format("Delegate-SetProperty-{0}-{1}", typeof(TSource).FullName, propertyName); SetPropertyValue
result = (SetPropertyValue
)myDelegateCache.GetOrAdd( key, newkey => { return Delegate.CreateDelegate(typeof(SetPropertyValue
), obj, typeof(TSource).GetProperty(propertyName).GetSetMethod()); } ); return result; }

注意,上面使用了.net 4.0的线程安全的字典来缓存生成的委托类型实例:

private static ConcurrentDictionary<string, Delegate> myDelegateCache = new ConcurrentDictionary<string, Delegate>();
好了,下面我们写一点测试代码:

 

CarInfo info = new CarInfo();            info.CID = 999;            ////            //info.ID==999;            SetPropertyValue
set = CreateSetPropertyValueDelegate
(info, "CID"); set(100);//100; GetPropertyValue
get = CreateGetPropertyValueDelegate
(info, "CID"); var r = get();//100 GetPropertyValue
get2 = CreateGetPropertyValueDelegate
(info, "CID"); var r2 = get2();//100

 

经测试,结果正常,这样,通用的最快的属性访问器就有了。

这个方法的性能怎么样?听说.net的字典查找性能不够高,继续写段测试代码跑跑看:

(注:测试代码增加了相应的NoCache方法,代码如下:

public static GetPropertyValue
CreateGetPropertyValueDelegateNoCache
(TSource obj, string propertyName) { return (GetPropertyValue
)Delegate.CreateDelegate(typeof(GetPropertyValue
), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod()); ; } public static SetPropertyValue
CreateSetPropertyValueDelegateNoCache
(TSource obj, string propertyName) { return (SetPropertyValue
)Delegate.CreateDelegate(typeof(SetPropertyValue
), obj, typeof(TSource).GetProperty(propertyName).GetSetMethod()); ; }

 

Console.WriteLine("{0, -15}{1,-10}{2,-8}{3,-8} {4,-8}", "Times", "直接访问", "委托(非缓存)", "委托(字典缓存)", "委托(变量缓存)");            //性能测试 直接访问            int times = 1000000;            var stopwatch = new Stopwatch();            stopwatch.Start();            for (int i = 0; i < times; i++)            {                int oldValue = info.CID;                info.CID = i;            }            var duration1 = stopwatch.ElapsedMilliseconds;            //使用委托,非缓存            stopwatch.Restart();            for (int i = 0; i < times; i++)            {                int oldValue = CreateGetPropertyValueDelegateNoCache
(info, "CID")(); CreateSetPropertyValueDelegateNoCache
(info, "CID")(i); } var duration2 = stopwatch.ElapsedMilliseconds; //使用委托,字典缓存 stopwatch.Restart(); for (int i = 0; i < times; i++) { int oldValue = CreateGetPropertyValueDelegate
(info, "CID")(); CreateSetPropertyValueDelegate
(info, "CID")(i); } var duration3 = stopwatch.ElapsedMilliseconds; //使用委托,直接缓存 stopwatch.Restart(); for (int i = 0; i < times; i++) { int oldValue = get(); set(i); } var duration4 = stopwatch.ElapsedMilliseconds; stopwatch.Stop(); Console.WriteLine("{0, -15} {1,-15} {2,-15} {3,-15} {4,-15} ", times, duration1, duration2, duration3, duration4); Console.WriteLine("--------------------单位(ms)--------------------------");

下面是运行结果:

===================Debug模式=====================Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         17              12421            949              16--------------------单位(ms)----------------------------------------------====================Release模式=========================Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         9               11696            867              8--------------------单位(ms)--------------------------

结果令人惊异:直接执行委托调用,比调用方法本身还要快点,另外也证明了,字典的查找性能的确不高。这个测试中字典元素的数量是较少的,有朋友提示,可能是计算字典的Key的哈希耗费了较多性能,于是将缓存字典的长度改小成DGP-{0}-{1} 和 DSP-{0}-{1},再次进行测试:

========================long key==================================Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         19              11817            971              18--------------------单位(ms)--------------------------Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         24              11210            882              16--------------------单位(ms)--------------------------Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         22              11168            895              16--------------------单位(ms)--------------------------========================short key==================================Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         20              11727            689              18--------------------单位(ms)--------------------------imes          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         18              11804            738              17-------------------单位(ms)--------------------------Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         23              11234            684              16--------------------单位(ms)--------------------------

测试结果表明,字典的Key的长度的确对性能有影响,所以我们需要注意Key不是越长越好。

 

补充:

下面有朋友回复说,要比较它在10次,100,10000,1000000 不同情况下面的效率,将测试程序稍微改写了下,下面是运行结果:

 

Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)10              0               3                0                0--------------------单位(ms)--------------------------*Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)100             0               1                0                0--------------------单位(ms)--------------------------*Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)10000           0               165              8                0--------------------单位(ms)--------------------------*Times          直接访问      委托(非缓存) 委托(字典缓存) 委托(变量缓存)1000000         31              11556            755              17--------------------单位(ms)--------------------------*

从测试来看,在执行次数在几百次的范围内,效率相差都是很小的,可以忽略,所以不用缓存委托结果也行。

 

它能做什麽?

在动态构设置对象的属性值的地方,比如ORM的实体类属性赋值,用途很大的。

 PS:今天测试发现,代码

Delegate.CreateDelegate(typeof(GetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod());

创建的委托方法仅针对当前实例对象 obj 有效,除非这是静态属性,它并不能作为一个通用类型的属性访问器,所以将它缓存意义不大,但可以作为优化属性访问的一个手段。

 

 ------------------分界线---------------------------

欢迎加入,做最轻最快的数据框架!

 

 

 

 

 

 

 

转载地址:http://ulbfl.baihongyu.com/

你可能感兴趣的文章
技术人玩转iPhone4S iOS 5.1.1完美越狱
查看>>
在敏捷测试中如何设计用例
查看>>
使用JSOM创建一个SharePoint网站计数器
查看>>
老硬盘华丽变身高速硬盘
查看>>
IT公司规模对技术人员知识增长率的影响
查看>>
由日企衰败联想到的运维管理问题
查看>>
Outlook中轻松添加LDAP服务
查看>>
线上咨询交易火爆 新模式还是新机遇?
查看>>
比较使用sql*loader的直接加载方式和传统加载方式的性能差异
查看>>
HAProxy Nginx LVS Apache总结篇
查看>>
BlackBerry Localization sample (1)
查看>>
类模版和函数模版需要注意的
查看>>
用 Tornado 实现简单的在线代理
查看>>
函数指针和指针函数
查看>>
HTML 如何让图片全屏的问题
查看>>
silverlight 如何在浏览器的新页面里打开一个xaml
查看>>
SQL Tuning Advisor使用实例
查看>>
server-U上传中文文件乱码
查看>>
编程珠玑:用后缀数组寻找最长重复字符串
查看>>
Java写到.txt文件,如何实现换行
查看>>