|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
也不知道,我同学昨天说数据挖掘很好。
- 换个思绪,最直白的完成体例
- 运转时天生代码
- 功能比拼
择要
纯真的反射带来天真性的同时,也年夜年夜下降了使用程序的效力。本文将使用C#的各类手艺,就怎样完成静态的办法挪用或属性会见做一些开端的研讨。但愿能够给一样必要进步反射功能的伴侣一些匡助。
成绩的笼统
反射能够用在良多的情形中,可是笼统来看就是用来会见编译时没法断定的成员。这成员能够是办法,也能够是属性。为了简化成绩,我们把成绩限制在属性的会见上。那末反射这个功效就能够笼统成上面这个接口。- ///<summary>///Abstractionofthefunctionofaccessingmemberofaobjectatruntime.///</summary>publicinterfaceIMemberAccessor{///<summary>///Getthemembervalueofanobject.///</summary>///<paramname="instance">Theobjecttogetthemembervaluefrom.</param>///<paramname="memberName">Themembername,couldbethenameofapropertyoffield.Mustbepublicmember.</param>///<returns>Themembervalue</returns>objectGetValue(objectinstance,stringmemberName);///<summary>///Setthemembervalueofanobject.///</summary>///<paramname="instance">Theobjecttogetthemembervaluefrom.</param>///<paramname="memberName">Themembername,couldbethenameofapropertyoffield.Mustbepublicmember.</param>///<paramname="newValue">Thenewvalueofthepropertyfortheobjectinstance.</param>voidSetValue(objectinstance,stringmemberName,objectnewValue);}
复制代码 上面我们就来切磋这个接口怎样完成才干到达最高效力。
没有优化的反射
利用反射是完成下面接口的最直不雅最复杂的体例。代码以下:- publicclassReflectionMemberAccessor:IMemberAccessor{publicobjectGetValue(objectinstance,stringmemberName){varpropertyInfo=instance.GetType().GetProperty(memberName);if(propertyInfo!=null){returnpropertyInfo.GetValue(instance,null);}returnnull;}publicvoidSetValue(objectinstance,stringmemberName,objectnewValue){varpropertyInfo=instance.GetType().GetProperty(memberName);if(propertyInfo!=null){propertyInfo.SetValue(instance,newValue,null);}}}
复制代码 可是这类体例的效力让人望而生畏。经由剖析我们能够发明最慢的部分就是GetValue和SetValue这两个挪用。
利用Delegate优化的反射
将PropertyInfo的XetValue代办署理起来是最复杂的进步功能办法。并且也已有良多人先容了这类体例,
1.FastDynamicPropertyFieldAccessors
2.晚绑定场景下对象属性赋值和取值能够不必要PropertyInfo
假如仅仅是看到他们的测试了局,会觉得晚绑定就能够让属性的静态会见的速率到达和间接取值一样的速率,会以为这生存何等优美啊。可是假如你真的把这个手艺用在个甚么中央会发明基本不是这么回事儿。实在的生存会如老赵写的FastReflectionLibrary中给出的测试了局一样平常。你会发明即便是晚绑定了或是Emit了,速率也是要比间接会见慢5-20倍。是老赵的完成体例有成绩吗?固然不是。
公允的比赛
这里明白一下我们要完成的功效是甚么?我们要完成的功效是,用一组办法或是形式,静态地会见任何一个对象上的任何一个属性。而后面那些看些优美的测试,都只是在测试晚绑定后的托付挪用的功能,而那测试用的晚绑定托付挪用都是针对某个类的某个属性的。这不是明摆着欺侮反射吗?固然测试用的反射Invoke也是针对一个属性,可是反射的通用版本的功能也是差未几的,Invoke才是损耗的年夜头。这也是数据统计蒙人的最多见的伎俩,用本人最好的一部分和对方的最差的一部分往对照。可是我们真正体贴的是全体。
用晚绑定这个特征往完成相似反射能完成的功效,是必要把每一个类的每一个属性都缓存起来,而且在利用的时分,依据以后对象的范例和所取的属性名查找对应的缓存好的晚绑定托付。这些功效在那些优美的测试了局中都完整没有表现出来。而老赵的FastReflectionLibary完成了这些功效,以是测试了局看上往要差良多。可是这才是实在的数据。
公允的完成体例
为了文章的完全起见,Delegate反射的完成体例以下。(我这里为了复杂起见,没有过量优化,假如你要用这个办法,仍是有很年夜的优化空间的。)
办法有两种,一种是利用Delegate.CreateDelegate函数。一种是利用ExpressionTree。
利用Delegate的中心代码分离以下所示:- internalclassPropertyAccessor<T,P>:INamedMemberAccessor{privateFunc<T,P>GetValueDelegate;privateAction<T,P>SetValueDelegate;publicPropertyAccessor(Typetype,stringpropertyName){varpropertyInfo=type.GetProperty(propertyName);if(propertyInfo!=null){GetValueDelegate=(Func<T,P>)Delegate.CreateDelegate(typeof(Func<T,P>),propertyInfo.GetGetMethod());SetValueDelegate=(Action<T,P>)Delegate.CreateDelegate(typeof(Action<T,P>),propertyInfo.GetSetMethod());}}publicobjectGetValue(objectinstance){returnGetValueDelegate((T)instance);}publicvoidSetValue(objectinstance,objectnewValue){SetValueDelegate((T)instance,(P)newValue);}}
复制代码 Delegate.CreateDelegate在利用上有一个请求,其天生的Delegate的署名必需与Method的声明分歧。以是就有了下面利用泛型的体例。每一个PropertyAccessor是针对特定属性的,要真正用起来还要用Dictionary做下Mapping。以下所示:- publicclassDelegatedReflectionMemberAccessor:IMemberAccessor{privatestaticDictionary<string,INamedMemberAccessor>accessorCache=newDictionary<string,INamedMemberAccessor>();publicobjectGetValue(objectinstance,stringmemberName){returnFindAccessor(instance,memberName).GetValue(instance);}publicvoidSetValue(objectinstance,stringmemberName,objectnewValue){FindAccessor(instance,memberName).SetValue(instance,newValue);}privateINamedMemberAccessorFindAccessor(objectinstance,stringmemberName){vartype=instance.GetType();varkey=type.FullName+memberName;INamedMemberAccessoraccessor;accessorCache.TryGetValue(key,outaccessor);if(accessor==null){varpropertyInfo=type.GetProperty(memberName);accessor=Activator.CreateInstance(typeof(PropertyAccessor<,>).MakeGenericType(type,propertyInfo.PropertyType),type,memberName)asINamedMemberAccessor;accessorCache.Add(key,accessor);}returnaccessor;}}
复制代码 用ExpressionTree的天生托付的时分,也会碰到范例的成绩,可是我们能够在ExpressionTree中对参数和前往值的范例举行处置,如许就不必要泛型的完成体例了。代码以下:- publicclassDelegatedExpressionMemberAccessor:IMemberAccessor{privateDictionary<string,Func<object,object>>getValueDelegates=newDictionary<string,Func<object,object>>();privateDictionary<string,Action<object,object>>setValueDelegates=newDictionary<string,Action<object,object>>();publicobjectGetValue(objectinstance,stringmemberName){vartype=instance.GetType();varkey=type.FullName+memberName;Func<object,object>getValueDelegate;getValueDelegates.TryGetValue(key,outgetValueDelegate);if(getValueDelegate==null){varinfo=type.GetProperty(memberName);vartarget=Expression.Parameter(typeof(object),"target");vargetter=Expression.Lambda(typeof(Func<object,object>),Expression.Convert(Expression.Property(Expression.Convert(target,type),info),typeof(object)),target);getValueDelegate=(Func<object,object>)getter.Compile();getValueDelegates.Add(key,getValueDelegate);}returngetValueDelegate(instance);}}
复制代码 一个优化体例是,把这个类做成泛型类,那末key就能够只是memberName,如许就少往了type.FullName及一次字符串拼接操纵。功能能够进步很多。可是这类托付式的会见就是功能上的极限了吗?假如是我就不必来写这篇文章了。
固然盗窟却更间接的办法
我们的方针是静态的会见一个对象的一个属性,一谈到静态老是会天然而然地想到反射。实在另有一个对照朴实的体例。就是让这个类本人去向理。还记得一入手下手界说的IMemberAccessor接口吗?假如我们一切的类的都完成了这个接口,那末就间接挪用这个办法就是了。体例以下。- publicclassMan:IMemberAccessor{publicstringName{get;set;}publicintAge{get;set;}publicDateTimeBirthday{get;set;}publicdoubleWeight{get;set;}publicdoubleHeight{get;set;}publicdecimalSalary{get;set;}publicboolMarried{get;set;}publicobjectGetValue(objectinstance,stringmemberName){varman=instanceasMan;if(man!=null){switch(memberName){case"Name":returnman.Name;case"Age":returnman.Age;case"Birthday":returnman.Birthday;case"Weight":returnman.Weight;case"Height":returnman.Height;case"Salary":returnman.Salary;case"Married":returnman.Married;default:returnnull;}}elsethrownewInvalidProgramException();}publicvoidSetValue(objectinstance,stringmemberName,objectnewValue){varman=instanceasMan;if(man!=null){switch(memberName){case"Name":man.Name=newValueasstring;break;case"Age":man.Age=Convert.ToInt32(newValue);break;case"Birthday":man.Birthday=Convert.ToDateTime(newValue);break;case"Weight":man.Weight=Convert.ToDouble(newValue);break;case"Height":man.Height=Convert.ToDouble(newValue);break;case"Salary":man.Salary=Convert.ToDecimal(newValue);break;case"Married":man.Married=Convert.ToBoolean(newValue);break;}}elsethrownewInvalidProgramException();}}
复制代码 有人大概会忧虑用这类体例,属性多了以后功能会下落。假如你用Reflector之类的工具反编译一下天生的DLL,你就不会有这类挂念了。C#关于switch语句有相称力度的优化。大略地讲,当属性少时会将switch天生为一堆ifelse。关于字段范例为string,也会主动地转成dictionary+int。
经由测试这类体例比下面的缓存晚绑定的体例要快一倍。可是优势也很分明,就是代码量太年夜了,并且不是一个好的计划,也不文雅。
用静态天生的工具函数让静态属性会见更快一些
下面的办法速率上实际上是最有上风的,可是缺少可操纵性。可是假如我们能为每一个类静态地天生两个Get/Set办法,那末这个办法就实践可用了。注重,这时候的静态挪用并非反射挪用了。天生的体例就是利用ExpressionTree编译出函数。
又由于这个体例是每一个类一个函数,不像之前的体例都是一个属性一个会见对象。我们就能够使用C#的另外一个特征来制止Dictionary的利用——泛型类中的静态成员:假如GenericClass<T>中界说的静态成员staticMember,那末GenericClass<A>中的staticMember和GenericClass<B>中的staticMember是不共享的。固然查找泛型类也必要分外的运转时事情,可是价值比Dictionary查询要低。
在这个办法中,既没有效到反射,也没有效到缓存Dictionary。能更好地包管与手工代码功能的分歧度。
完成的代码以下,鉴于代码量,只列出了get办法的代码:- publicclassDynamicMethod<T>:IMemberAccessor{internalstaticFunc<object,string,object>GetValueDelegate;publicobjectGetValue(objectinstance,stringmemberName){returnGetValueDelegate(instance,memberName);}staticDynamicMethod(){GetValueDelegate=GenerateGetValue();}privatestaticFunc<object,string,object>GenerateGetValue(){vartype=typeof(T);varinstance=Expression.Parameter(typeof(object),"instance");varmemberName=Expression.Parameter(typeof(string),"memberName");varnameHash=Expression.Variable(typeof(int),"nameHash");varcalHash=Expression.Assign(nameHash,Expression.Call(memberName,typeof(object).GetMethod("GetHashCode")));varcases=newList<SwitchCase>();foreach(varpropertyInfointype.GetProperties()){varproperty=Expression.Property(Expression.Convert(instance,typeof(T)),propertyInfo.Name);varpropertyHash=Expression.Constant(propertyInfo.Name.GetHashCode(),typeof(int));cases.Add(Expression.SwitchCase(Expression.Convert(property,typeof(object)),propertyHash));}varswitchEx=Expression.Switch(nameHash,Expression.Constant(null),cases.ToArray());varmethodBody=Expression.Block(typeof(object),new[]{nameHash},calHash,switchEx);returnExpression.Lambda<Func<object,string,object>>(methodBody,instance,memberName).Compile();}}
复制代码 可是,好吧,成绩来了。泛型类就意味着必要在写代码的时分,大概说编译时晓得对象的范例。如许也不切合我们一入手下手界说的方针。固然办理计划也是有的,就是再把谁人Dictionary缓存请返来。详细体例参考下面的给Delegate做缓存的代码。
另有一个成绩就是,这类Switch代码的功能会跟着Property数目的增加而出现大抵为线性的下落。会终极差于Delegate缓存体例的挪用。可是幸亏这个临界点对照高,大抵在40个到60个属性摆布。
功能测试
我们先把一切的体例列一下。
- 间接的对象属性读写
- 纯真的反射
- 利用Delegate.CreateDelegate天生托付并缓存
- 利用ExpressionTree天生属性会见托付并缓存
- 让对象本人完成IMemberAccessor接口,利用SwitchCase。
- 为每一个类天生IMemberAcessor接口所界说的函数。(非泛型体例挪用)
- 为每一个类天生IMemberAcessor接口所界说的函数。(泛型体例挪用)
我们来看一下这6种完成对应的7种利用体例的功能。
Debug:实行1000万次
办法第一次了局第二次了局间接挪用208ms227ms反射挪用21376ms21802msExpression托付4341ms4176msCreateDelegate托付4204ms4111ms对象本身Switch1653ms1338ms静态天生函数2123ms2051ms(泛型)静态天生函数1167ms1157msRelease:实行1000万次
办法第一次了局第二次了局间接挪用73ms77ms反射挪用20693ms21229msExpression托付3852ms3853msCreateDelegate托付3704ms3748ms对象本身Switch1105ms1116ms静态天生函数1678ms1722ms(泛型)静态天生函数843ms862ms静态天生的函数比手写的Switch还要快的缘故原由是手写的Switch要利用到Dictionary来将String范例的字段名映照到int值。而我们天生的Switch是间接利用属性的hashcode值举行的。以是会更快。完全的代码能够从这里下载到。
以前学了大概半年时间的asp(没有机会做大系统,最多是自己对公司系统做些调整和修改还有一些小程序)。应该说开始接触asp.net是今年元月5号的事。现在很想把公司的系统重新用.net来架构,却不知道如何下手。 |
|