打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
使用泛型委托,构筑最快的通用属性访问器

使用泛型委托,构筑最快的通用属性访问器

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

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

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

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

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

        private static ConcurrentDictionary<string, Delegate> myDelegateCache = new ConcurrentDictionary<string, Delegate>();        public static GetPropertyValue<T> CreateGetPropertyValueDelegate<TSource, T>(TSource obj, string propertyName)        {            string key = string.Format("Delegate-GetProperty-{0}-{1}", typeof(TSource).FullName, propertyName);            GetPropertyValue<T> result = (GetPropertyValue<T>)myDelegateCache.GetOrAdd(                key,                 newkey =>                {                    return Delegate.CreateDelegate(typeof(GetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod());                }                );            return result;        }        public static SetPropertyValue<T> CreateSetPropertyValueDelegate<TSource, T>(TSource obj, string propertyName)        {            string key = string.Format("Delegate-SetProperty-{0}-{1}", typeof(TSource).FullName, propertyName);            SetPropertyValue<T> result = (SetPropertyValue<T>)myDelegateCache.GetOrAdd(               key,               newkey =>               {                   return Delegate.CreateDelegate(typeof(SetPropertyValue<T>), 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<int> set = CreateSetPropertyValueDelegate<CarInfo, int>(info, "CID");            set(100);//100;            GetPropertyValue<int> get = CreateGetPropertyValueDelegate<CarInfo, int>(info, "CID");            var r = get();//100            GetPropertyValue<int> get2 = CreateGetPropertyValueDelegate<CarInfo, int>(info, "CID");            var r2 = get2();//100

 

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

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

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

 public static GetPropertyValue<T> CreateGetPropertyValueDelegateNoCache<TSource, T>(TSource obj, string propertyName)        {            return (GetPropertyValue<T>)Delegate.CreateDelegate(typeof(GetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod()); ;        }        public static SetPropertyValue<T> CreateSetPropertyValueDelegateNoCache<TSource, T>(TSource obj, string propertyName)        {            return (SetPropertyValue<T>)Delegate.CreateDelegate(typeof(SetPropertyValue<T>), 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<CarInfo, int>(info, "CID")();                CreateSetPropertyValueDelegateNoCache<CarInfo, int>(info, "CID")(i);            }            var duration2 = stopwatch.ElapsedMilliseconds;            //使用委托,字典缓存            stopwatch.Restart();            for (int i = 0; i < times; i++)            {                int oldValue = CreateGetPropertyValueDelegate<CarInfo, int>(info, "CID")();                CreateSetPropertyValueDelegate<CarInfo, int>(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)--------------------------

结果令人惊异:直接执行委托调用,比调用方法本身还要快点,另外也证明了,字典的查找性能的确不高。

 

它能做什麽?

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

 

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

欢迎加入PDF.NET开源技术团队,做最轻最快的数据框架!

 

 

 

 

 

 

 

分类: 编程语言
标签: 委托, 泛型
2
0
(请您对文章做出评价)
博主上一篇:领域驱动设计(DDD)技术分享
首页上一篇:多线程编程答案篇(C#和Java):解决pull和push模式在同一个程序中的冲突
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
三种属性操作性能比较:PropertyInfo Expression Tree Delegate.CreateDelegate - - 博客园
【死磕 Spring】----- IOC 之分析 BeanWrapper
C# linq查询 动态OrderBy
浅谈.NET反射机制的性能优化
winform控件开发总结
SQL Server 批量插入数据的两种方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服