Создает делегата из MethodInfo? - PullRequest
32 голосов
/ 14 июля 2009

После поиска в Google и посадки на SO и прочтения этот другой вопрос

Можно ли построить правильный делегат из MethodInfo , если вы не знали число или типы параметров во время компиляции?

Подробнее об этом: можно ли сделать это элегантно без использования Reflection.Emit или компоновщиков типов?

Для меня это просто облом, потому что Delegate.CreateDelegate требует, чтобы я указывал правильный тип Делегата в качестве первого параметра, иначе он будет генерировать исключения или вызовет неправильный метод.

Я создаю некоторые механизмы для ниндзя, и это очень поможет ... Спасибо!


Вот общее решение:

/// <summary>
/// Builds a Delegate instance from the supplied MethodInfo object and a target to invoke against.
/// </summary>
public static Delegate ToDelegate(MethodInfo mi, object target)
{
    if (mi == null) throw new ArgumentNullException("mi");

    Type delegateType;

    var typeArgs = mi.GetParameters()
        .Select(p => p.ParameterType)
        .ToList();

    // builds a delegate type
    if (mi.ReturnType == typeof(void)) {
        delegateType = Expression.GetActionType(typeArgs.ToArray());

    } else {
        typeArgs.Add(mi.ReturnType);
        delegateType = Expression.GetFuncType(typeArgs.ToArray());
    }

    // creates a binded delegate if target is supplied
    var result = (target == null)
        ? Delegate.CreateDelegate(delegateType, mi)
        : Delegate.CreateDelegate(delegateType, target, mi);

    return result;
}

Примечание : я создаю приложение Silverlight, которое заменит приложение javascript много лет назад, в котором у меня есть несколько интерфейсов Javascript, которые вызывают один и тот же метод Silverlight [ScriptableMember].

Необходимо поддерживать все эти устаревшие интерфейсы JS, а также новый интерфейс для доступа к новым функциям, поэтому то, что автоматически настраивает интерфейс JS и «делегирует» вызов правильному методу Silverlight, поможет значительно ускорить работу.

Я не могу разместить здесь код, так что это резюме.

Ответы [ 3 ]

22 голосов
/ 14 июля 2009

Если честно, если вы не знаете тип во время компиляции, то нет огромной выгоды в создании Delegate. Вы не хотите использовать DynamicInvoke; это будет примерно так же медленно, как отражение. Основным исключением является случай, когда в тени скрывается тип делегата, например, при подписке на событие - в этом случае EventInfo делает это доступным.

Для информации, в .NET 3.5 на Expression есть:

Expression.GetActionType(params Type[] typeArgs);
Expression.GetFuncType(params Type[] typeArgs)

Это может помочь до такой степени:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
static class Program {
    static void Main() {
        DoStuff("Test1");
        DoStuff("Test2");
    }
    static void DoStuff(string methodName) {
        MethodInfo method = typeof(Program).GetMethod(methodName);
        List<Type> args = new List<Type>(
            method.GetParameters().Select(p => p.ParameterType));
        Type delegateType;
        if (method.ReturnType == typeof(void)) {
            delegateType = Expression.GetActionType(args.ToArray());
        } else {
            args.Add(method.ReturnType);
            delegateType = Expression.GetFuncType(args.ToArray());
        }
        Delegate d = Delegate.CreateDelegate(delegateType, null, method);
        Console.WriteLine(d);
    }
    public static void Test1(int i, DateTime when) { }
    public static float Test2(string x) { return 0; }
}
7 голосов
/ 14 июля 2009

Если вы не знаете заранее количество или тип параметров, возможно, это означает, что вы не знаете тип делегата, который хотите создать?

Если это так, вы застряли в абсолютно общем случае.

Однако, для большинства распространенных случаев (без параметров ref / out, достаточно мало параметров, чтобы использовать один из существующих типов), вам может не понравиться один из делегатов Func или Action. (.NET 4.0 имеет Func / Action типов для огромного количества параметров, так что на самом деле вам нужно беспокоиться только о параметрах out / ref.) Если метод имеет тип возврата, отличный от void, используйте Func, в противном случае используйте Action. Определите, какой тип использовать, основываясь на количестве параметров, например,

static readonly Type[] FuncTypes = { typeof(Func), 
    typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), /* etc */ };

Используйте Type.MakeGenericType, используя типы параметров и тип возвращаемого значения, чтобы получить правильный тип делегата, тогда Delegate.CreateDelegate должен работать.

У меня сейчас нет времени на подготовку образца, но дайте мне знать, если вы хотите, чтобы я позже.

Один вопрос: как вы собираетесь использовать этого делегата? Что-то еще нужно знать, как выполнить это, конечно ...

6 голосов
/ 28 декабря 2010

Почему это сложно?

public static Delegate CreateDelegate(this MethodInfo method)
{
    return Delegate.CreateDelegate
    (
        Expression.GetDelegateType
        (
            method.GetParameters()
                .Select(p => p.ParameterType)
                .Concat(new Type[] { method.ReturnType })
                .ToArray()
        ),
        null,
        method
    );   
}

[Примечание: у меня есть префикс этого метода «Создать ...». «Кому ...» вводит в заблуждение, поскольку вводит вас в заблуждение думать, что это обращение.]

...