原文:http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker
介绍
有时,我们会碰见需要动态调用对象方法的场景,而这个方法只有在运行的时候才能得知。通常的,会使用方法的反射调用,但是这通常会导致程序速度变慢。这篇文章将介绍一种高效替代方案----动态方法调用。
背景环境
当我读到文章《Fast Dynamic Property Accessors》时,我想到我项目中在循环中运用了大量方法的反射调用,然而这样调用是毫无效率的。DynamicMethod
提醒我是否可在方法调用前使用 System.Reflection.Emit 生成DynamicMethod
绑定到指定的方法,这样或许能提高程序性能。
代码
首先,使用反射找到将要调用的方法成员:
MethodInfo methodInfo = typeof(Person).GetMethod("Say");
然后,创建动态方法并且返回调用该动态方法的委托:
FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo); fastInvoker(new Person(), new object[]{"hello"});
代替之前方法的反射调用:
methodInfo.Invoke(new Person(), new object[]{"hello"});
实现
首先,我们需要为动态方法定义一个适当的委托:
public delegate object FastInvokeHandler(object target, object[] paramters);
为了不改变之前方法的反射调用模式,所以我们定义的委托接收参数和返回值类似MethodInfo.invoke() 。
下面贴出 DynamicMethod 生成代码:
public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo) { DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module); ILGenerator il = dynamicMethod.GetILGenerator(); ParameterInfo[] ps = methodInfo.GetParameters(); Type[] paramTypes = new Type[ps.Length]; for (int i = 0; i < paramTypes.Length; i++) { paramTypes[i] = ps[i].ParameterType; } LocalBuilder[] locals = new LocalBuilder[paramTypes.Length]; for (int i = 0; i < paramTypes.Length; i++) { locals[i] = il.DeclareLocal(paramTypes[i]); } for (int i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldarg_1); EmitFastInt(il, i); il.Emit(OpCodes.Ldelem_Ref); EmitCastToReference(il, paramTypes[i]); il.Emit(OpCodes.Stloc, locals[i]); } il.Emit(OpCodes.Ldarg_0); for (int i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldloc, locals[i]); } il.EmitCall(OpCodes.Call, methodInfo, null); if (methodInfo.ReturnType == typeof(void)) il.Emit(OpCodes.Ldnull); else EmitBoxIfNeeded(il, methodInfo.ReturnType); il.Emit(OpCodes.Ret); FastInvokeHandler invoder = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler)); return invoder; }
总结
好了,我想这个通用方法可以代替大多数方法的反射同时会获得大约50倍的效率提高,欢迎反馈任何改善的建议。
另外值得注意的优势(感谢MaxGuernsey的提醒):如果你调用的方法内部抛出异常,FastInovker
会抛出具体错误信息,然而Method.Invoke
仅仅只会抛出“调用目标发生异常(TargetInvocationException)”。
整理了一篇文章对该篇进行补充及扩展,欢迎查看《《(译)一个通用快速的反射方法调用》续篇》
相关链接: