Какой самый быстрый способ в .Net 3.5 вызвать метод по имени строки? - PullRequest
7 голосов
/ 23 сентября 2011

Итак, очевидный способ сделать это ..

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

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

И я могу добавить кэширование, чтобы сохранить методы, но мне интересно, есть ли совершенно другой и более быстрый способ?

1 Ответ

15 голосов
/ 23 сентября 2011

Самый быстрый способ - кэшировать типизированный делегат; если вы знаете, подпись всегда:

void PersonInstance.MethodName(string s);

Тогда вы можете создать Action<Person,string> через Delegate.CreateDelegate:

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

Затем его можно кэшировать против имени и вызывать как:

action(personInstance, value);

Обратите внимание, что кеш здесь имеет решающее значение; Отражение в том, чтобы найти метод и подготовить типизированный делегат, нетривиально.

Сложнее, если подпись непредсказуема, поскольку DynamicInvoke относительно медленный. самый быстрый способ - использовать DynamicMethod и ILGenerator для написания метода shim (на лету), который принимает объект [] для параметров, распаковывает и приводит его в соответствие с сигнатурой - тогда вы можете сохранить Action<object, object[]> или Func<object,object[],object>. Это, однако, сложная тема. Я мог бы привести пример, если это действительно необходимо. По сути, запись (во время выполнения):

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

Вот пример этого (примечание: он не обрабатывает ref / out аргументов в данный момент и, возможно, несколько других сценариев - и я оставил сторону «кэша» вещей как упражнение для читателя):

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...