Вопрос по .net, delegates, methodinfo, reflection – Создает делегата из MethodInfo?

32

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

Is it possible to build a correct Delegate from a MethodInfo if you didn't know the number or types of parameters at compile time?

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

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

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

Here's a generic solution:

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

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

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

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

Ваш Ответ

3   ответа
22

Если честно, если вы не знаете тип во время компиляции, то не будет огромного количества преимуществ в создании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; }
}
@Saranya, чтобы вызвать егоquickly (с помощьюInvoke), вам нужно знать сигнатуру метода заранее, чтобы вы могли привести его к правильному типу - например,Action<Foo>илиFunc<Bar, string, int>.
Вау .... это очень помогло! Вы, ребята, рок! chakrit
Хорошо. Спасибо Марк Гравелл.
@ Марк Грэвелл, я не могу вызвать делегата, созданного в приведенном выше коде, как d (). После поиска в Google я обнаружил, что dynamicInvoke может использоваться для вызова метода, который очень медленный. Пожалуйста, помогите. я новичок для делегатов и событий. мое требование - вызывать метод динамически, число или тип параметров будут известны только во время выполнения
Я строю это, чтобы склеить Silverlight [ScriptableMember] и отдельный интерфейс Javascript, поэтому мне не нужно беспокоиться о синхронизации сигнатур методов в обоих местах. chakrit
7

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

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

Однако для большинстваcommon случаев (без параметров 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 должно сработать.

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

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

Чтобы избежать статического Типа [], рассмотрите Expression.GetActionType / Expression.GetFuncType - см. Пост. я мог быhope что эти методы были расширены, чтобы включить новые варианты .NET 4.0.
Круто - не знал о Expression.GetActionType / GetFuncType. Froody.
Я добавил "почему" на вопрос chakrit
ах-ха ... хороший пример для MakeGenericType + Func ... который бы это сделал :-) chakrit
6

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

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

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

Передавая объект в качестве второго параметра (называемого «firstArgument» - здесь я передаю ноль), вы можете указать объект, с которым связан делегат. Или я упускаю суть?
Expression.GetDelegateType Метод на самом деле специфичен для .NET 4 и SL 4. Мой вопрос был задан до выпуска .NET4 и SL4. В любом случае спасибо за ответ. Обратите внимание, что вам нужно будет привязать делегат к цели, если метод является методом экземпляра, поэтому обязательная часть все еще обязательна. chakrit

Похожие вопросы