Подтвердить что ты не робот

Создает делегат из MethodInfo?

После поиска и посадки на 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, поможет значительно ускорить работу.

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

4b9b3361

Ответ 1

Если честно, если вы не знаете тип во время компиляции, нет никакого большого преимущества при создании 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; }
}

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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

[Боковое примечание: я префикс этого метода "Создать...". "To..." сбивает с толку, поскольку он вводит вас в заблуждение, чтобы думать, что это конверсия.]