65.9K
CodeProject 正在变化。 阅读更多。
Home

通用的快速方法调用器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (73投票s)

2006 年 6 月 27 日

1分钟阅读

viewsIcon

276159

downloadIcon

3095

方法反射调用很方便,但通常情况下会非常慢。本文描述了一种动态方法调用的替代方法。

Sample Image - FastMethodInvoker.gif

引言

有时,我需要动态调用对象的某个方法,而实际方法可能直到运行时才可知。通常,反射很方便,但频繁使用会太慢。本文描述了一种动态方法调用的替代方法。

背景

当我阅读 快速动态属性访问器 这篇文章时,我想到我的项目中有大量的循环反射方法。但这些是方法,而不是属性。但 DynamicMethod 提醒我,也许我可以使用 Emit 生成一个 DynamicMethod 来绑定一个特定的方法,然后再调用它。我希望这能提高性能。

Using the Code

首先,我反射获取将被调用的方法

MethodInfo methodInfo = typeof(Person).GetMethod("Say");

然后,我获取 MethodInvoker 来调用

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);

它看起来与 MethodInfoInvoke 方法相同。是的,这意味着我可以使用相同的代码来像过去一样使用它。

这段代码生成 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

历史

  • 2006-7-05:更新以添加静态方法支持。感谢 Manuel Abadia。
  • 2006-6-30:更新以添加 ref/out 参数支持。感谢 Roger 的好建议。
© . All rights reserved.