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

     =============C#.Net 篇目录==============

 

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

你可能感兴趣的:(方法调用)