【问题标题】:What is the fastest way in .Net 3.5 to call a method by string name?.Net 3.5 中通过字符串名称调用方法的最快方法是什么?
【发布时间】:2011-11-23 18:47:03
【问题描述】:

所以显而易见的方法是......

var handler = GetType().GetMethod(methodName, BindingFlags.NonPublic |
                                              BindingFlags.Instance);

handler.Invoke(this, new object[] {e});

我可以添加缓存来保留这些方法,但我想知道是否有完全不同且更快的方法?

【问题讨论】:

    标签: .net performance .net-3.5


    【解决方案1】:

    最快的方法是缓存一个类型化的委托;如果你知道签名总是:

    void PersonInstance.MethodName(string s);
    

    然后你可以通过 Delegate.CreateDelegate 创建一个Action<Person,string>

    var action = (Action<Person,string>)Delegate.CreateDelegate(
        typeof(Action<Person,string>), method);
    

    然后可以根据名称缓存它,并调用为:

    action(personInstance, value);
    

    注意这里的缓存很关键;定位方法和准备类型委托的反射是非常重要的。

    如果签名是不可预测的,那就更难了,因为 DynamicInvoke 相对较慢。 最快 方法是使用 DynamicMethod 和 ILGenerator 编写一个 shim 方法(即时),该方法采用 object[] 作为参数并解包并将其强制转换为匹配签名 - 然后您可以存储Action&lt;object, object[]&gt;Func&lt;object,object[],object&gt;。然而,这是一个高级主题。如果真的需要,我可以提供一个例子。本质上是写(在运行时):

    void DummyMethod(object target, object[] args) {
        ((Person)target).MethodName((int)args[0],(string)args[1]);
    }
    

    这是一个这样做的例子(注意:它目前不处理 ref/out args,可能还有其他一些场景 - 我已经将事物的“缓存”方面作为读者练习):

    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    
    class Program
    {
        static void Main()
        {
            var method = typeof(Foo).GetMethod("Bar");
            var func = Wrap(method);
            object[] args = { 123, "abc"};
            var foo = new Foo();
            object result = func(foo, args);
        }
    
        static Func<object, object[], object> Wrap(MethodInfo method)
        {
            var dm = new DynamicMethod(method.Name, typeof(object), new Type[] {
                typeof(object), typeof(object[])
            }, method.DeclaringType, true);
            var il = dm.GetILGenerator();
    
            if (!method.IsStatic)
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Unbox_Any, method.DeclaringType);
            }
            var parameters = method.GetParameters();
            for (int i = 0; i < parameters.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Ldc_I4, i);
                il.Emit(OpCodes.Ldelem_Ref);
                il.Emit(OpCodes.Unbox_Any, parameters[i].ParameterType);
            }
            il.EmitCall(method.IsStatic || method.DeclaringType.IsValueType ?
                OpCodes.Call : OpCodes.Callvirt, method, null);
            if (method.ReturnType == null || method.ReturnType == typeof(void))
            {
                il.Emit(OpCodes.Ldnull);
            }
            else if (method.ReturnType.IsValueType)
            {
                il.Emit(OpCodes.Box, method.ReturnType);
            }
            il.Emit(OpCodes.Ret);
            return (Func<object, object[], object>)dm.CreateDelegate(typeof(Func<object, object[], object>));
        }
    }
    
    public class Foo
    {
        public string Bar(int x, string y)
        {
            return x + y;
        }
    }
    

    【讨论】:

    • 我想看一个使用DynamicMethod和ILGenerator的例子(如果不是太麻烦的话)。
    • @StriplingWarrior 不是问题,不 - 但是,我目前在 iPod 上,它不适合编写 IL-emit 代码。当我在 PC 上时,我可以在几个小时内添加一个示例吗?
    • @StriplingWarrior - 现在添加
    • @MarcGravell:甜蜜。我一定会将Wrap 方法添加到我的工具带中。
    • @StriplingWarrior 为这个答案被一起破解;添加您自己的单元测试等
    猜你喜欢
    • 2015-04-30
    • 2023-03-15
    • 1970-01-01
    • 2016-08-09
    • 2021-06-25
    • 2013-08-22
    • 2011-09-26
    • 2018-07-05
    • 1970-01-01
    相关资源
    最近更新 更多