IT技术交流替代反射调用的几种方式及

园子里和这个话题的相关文章比较多,本文是旧话重提,外加个小的总结。主要因为近期看到很多同事、朋友都已经使用VS进行.NET4.5开发了,却还在大量使用反射,不知道用新的方式。或有所了解,但又害怕性能不好不敢大胆去用。

本文以如下类为例:

publicclassMyMath{publicintAdd(inta,intb){returna+b;}}替代反射的几种方式

倒序说吧,从最先进最简单的开始。

1.dynamic调用

.NET4引入了dynamic类型,可以使用如下方式来完成对MyMath.Add方法的动态调用:

dynamicmath=newMyMath();intresult=math.Add(1,2);

非常简单,效率也不错,可以看后面的性能对比测试结果。

但有一点要注意,dynamic遵守.NET的访问级别限定,会对成员进行可见性检查。也就是说,只能dynamic调用public成员;当然,如果是同一程序集内部,internal成员也是可以访问的。

2.ExpressionTree编译调用

ExpressionTree是.NET3.5引入的。简单地,我们可以使用lambda构建一颗ExpressionTree:

varmath=newMyMath();ExpressionFuncint,int,intadd=(a,b)=math.Add(a,b);

这种方法适合手工编码构建,还有另外一种方式可以动态构建:

varadd=typeof(MyMath).GetMethod("Add");varmath=Expression.Parameter(typeof(MyMath));vara=Expression.Parameter(typeof(int),"a");varb=Expression.Parameter(typeof(int),"b");varbody=Expression.Call(myMath,add,a,b);varlambda=Expression.LambdaFuncMyMath,int,int,int(body,math,a,b);

两种方式构建出的Tree是相同的。

话归正题,构建出表达式树后,调用其Compile方法便可编译成一个委托,如下代码第3行:

varmath=newMyMath();ExpressionFuncint,int,intaddExpTree=(a,b)=math.Add(a,b);//ExressionTreeFuncint,int,intadd=addExpTree.Compile();//编译成委托varresult=add(1,2);//相加,结果为3

与dynamic调用方法同,ExpressionTree编译出的委托方法也遵守.NET的访问级别限定,会对成员进行可见性检查,不能访问私有成员。

3.反射发出调用

这里只介绍反射发出的一项技术DynamicMethod,.NET2.0新增此类。

使用DynamicMethod类在运行时定义轻量全局方法,然后使用委托执行这些方法。

针对MyMath.Add方法,调用比前面两种方式复杂些:

varaddMethod=typeof(MyMath).GetMethod("Add");vardynamicMethod=newDynamicMethod("",typeof(int),new[]{typeof(MyMath),typeof(int),typeof(int)});//varil=dynamicMethod.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Ldarg_2);il.Emit(OpCodes.Callvirt,addMethod);il.Emit(OpCodes.Ret);//varadd=(FuncMyMath,int,int,int)dynamicMethod.CreateDelegate(typeof(FuncMyMath,int,int,int));//varmath=newMyMath();varresult=add(math,1,2);

从第5行起,使用几个IL汇编指令,简单一说:

第5行,OpCodes.Ldarg_0是将索引为0的参数值推送到堆栈上,Ldarg_1、Ldarg_2以此类推;

第6行,OpCodes.Callvirt是调用对象的(后期绑定)方法,并且将返回值推送到计算堆栈上;

第9行,OpCodes.Ret表达从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。

反射发出是在汇编级别的,很底层,也就意味着效率更高、威力更强大。反射发出能绕过跳过JIT可见性检查,访问private成员(对于DynamicMethod类,请查看:DynamicMethod构造函数(String,Type,Type[],Boolean))。

下面是几种方法的性能测试。

性能对比测试

这里对直接、反射发出、dynamic、表达式树编译、反射五种调用方式进行性能对比测试。

测试结果

先给出测试的结果:

从上图中可以看出:

直接调用性能最佳;

反射发出和表达式树两种方式性能相当,速度接近直接调用;

dynamic性能居中,也不错;

反射方式性能最差。

另外说明两点:

本次测试仅针对MyMath.Add方法,其参数和返回值都是值类型,反射调用时存在大量装箱、拆箱。如果测试方法的参数和返回值都是引用类型,反射方式与其它方式间的差距会小些。

从上图可以看出这几次方式性能差别较大,但此结果是重复万次的情况下得出的。考虑单次调用,反射只比直接调用慢纳秒。如果你的代码不是位于循环的中心或是系统的瓶颈,调用次数不多,性能差异可以完全忽略。

测试代码

以下是测试用代码,仅参考:

usingSystem;usingSystem.Linq.Expressions;usingSystem.Reflection.Emit;publicclassMyMath{publicintAdd(inta,intb){returna+b;}}classProgram{staticvoidMain(string[]args){intresult;varmath=newMyMath();varcount=0000;Console.WriteLine("数据量:"+count);Console.WriteLine("-----------------------------r\n");using(Profiler.Step("循环:{0}ms")){for(inti=0;icount;i++)result=1;}using(Profiler.Step("直接调用:{0}ms")){for(inti=0;icount;i++)result=math.Add(i,i);}using(Profiler.Step("反射发出:{0}ms")){varemitAdd=BuildEmitAddFunc();for(inti=0;icount;i++)result=emitAdd(math,i,i);}using(Profiler.Step("表达式树:{0}ms")){varexpressionAdd=BuildExpressionAddFunc();for(inti=0;icount;i++)result=expressionAdd(math,i,i);}using(Profiler.Step("dynamic调用:{0}ms")){dynamicd=math;for(inti=0;icount;i++)result=d.Add(i,i);}using(Profiler.Step("反射调用:{0}ms")){varadd=typeof(MyMath).GetMethod("Add");for(inti=0;icount;i++)result=(int)add.Invoke(math,newobject[]{i,i});}Console.WriteLine("\r\n\r\n测试完成,任意键退出...");Console.ReadKey();}staticFuncMyMath,int,int,intBuildExpressionAddFunc(){varadd=typeof(MyMath).GetMethod("Add");varmath=Expression.Parameter(typeof(MyMath));vara=Expression.Parameter(typeof(int),"a");varb=Expression.Parameter(typeof(int),"b");varbody=Expression.Call(math,add,a,b);varlambda=Expression.LambdaFuncMyMath,int,int,int(body,math,a,b);returnlambda.Compile();}staticFuncMyMath,int,int,intBuildEmitAddFunc(){varadd=typeof(MyMath).GetMethod("Add");vardynamicMethod=newDynamicMethod("",typeof(int),new[]{typeof(MyMath),typeof(int),typeof(int)});varil=dynamicMethod.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Ldarg_2);il.Emit(OpCodes.Callvirt,add);il.Emit(OpCodes.Ret);return(FuncMyMath,int,int,int)dynamicMethod.CreateDelegate(typeof(FuncMyMath,int,int,int));}}

Profiler是我写的一个类,用于简化测试:

usingSystem;usingSystem.Diagnostics;publicclassProfiler:IDisposable{privateStopwatchwatch;privatestringmessage;privateProfiler(stringmessage){this.watch=newStopwatch();this.watch.Start();this.message=message;}publicvoidDispose(){watch.Stop();Console.WriteLine(message,watch.ElapsedMilliseconds);Console.WriteLine();}publicstaticIDisposableStep(stringmessage){returnnewProfiler(message);}publicstaticTInlineT(stringmessage,FuncTfunc){using(newProfiler(message))returnfunc();}}

如对其他内容感兴趣,或有更好建议欢迎在文章最下方留言给我们,我们平台将分享更多您







































白癜风发病原因有哪些
呼和浩特最好的白癜风医院


转载请注明地址:http://www.shiquanren.net/itcj/14124.html


  • 上一篇文章:
  • 下一篇文章: 没有了
  • 公司简介 广告合作 发布优势 服务条款 隐私保护 网站地图 版权声明