原文: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)”。
整理了一篇文章對該篇進行補充及擴展,歡迎查看《《(譯)一個通用快速的反射方法調用》續篇》
相關鏈接: