What is the fastest way in .Net 3.5 to call a method by string name?

前端 未结 1 575
南方客
南方客 2021-02-01 11:00

So the obvious way to do this is..

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


        
相关标签:
1条回答
  • 2021-02-01 11:18

    The fastest way would be to cache a typed delegate; if you know the signature is always:

    void PersonInstance.MethodName(string s);
    

    Then you could create an Action<Person,string> via Delegate.CreateDelegate:

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

    This can then be cached against the name, and invoked as:

    action(personInstance, value);
    

    Note that the cache here is critical; the reflection to locate the method and prepare a typed-delegate is non-trivial.

    It gets harder if the signature is unpredictable, as DynamicInvoke is relatively slow. The fastest way would be to use DynamicMethod and ILGenerator to write a shim method (on the fly) that takes an object[] for the parameters and unpacks and casts it to match the signature - then you can store an Action<object, object[]> or Func<object,object[],object>. That is, however, an advanced topic. I could provide an example if really needed. Essentially writing (at runtime):

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

    Here's an example of doing that (note: it doesn't handle ref/out args at the moment, and possibly a few other scenarios - and I've left the "cache" side of things as an exercise for the reader):

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题