(譯)一個通用快速的反射方法調用


 

原文:http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker

 

源碼下載:示例代碼_for_一個通用快速的反射方法調用

image

介紹

         有時,我們會碰見需要動態調用對象方法的場景,而這個方法只有在運行的時候才能得知。通常的,會使用方法的反射調用,但是這通常會導致程序速度變慢。這篇文章將介紹一種高效替代方案----動態方法調用。

 

背景環境

         當我讀到文章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)”。

 

         整理了一篇文章對該篇進行補充及擴展,歡迎查看《《(譯)一個通用快速的反射方法調用》續篇》

 

作者:Luyan

 

      相關鏈接:

                   《(9)程序集的加載和反射》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM