(译)一个通用快速的反射方法调用

 

原文: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)程序集的加载和反射》

原文地址:https://www.cnblogs.com/heyuquan/p/2430720.html