Buduje delegata z MethodInfo?

Po wygooglowaniu i wylądowaniu na SO i przeczytaniu tego drugiego pytania

Czy jest możliwe zbudowanie poprawnego delegata z MethodInfo jeśli nie znałeś liczby lub typów parametrów w czasie kompilacji?

[1]} więcej na ten temat: czy można to zrobić elegancko bez użycia refleksji.Emitować czy typować?

To jest dla mnie trochę przykre, ponieważ Delegat.CreateDelegate wymaga ode mnie podania poprawnego typu delegata jako pierwszy parametr albo wyrzuci wyjątki lub wywoła niepoprawną metodę.

Buduję kilka ninja gears i to bardzo by pomogło... Dzięki!

Oto ogólne rozwiązanie:

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

Notatka : buduję aplikację Silverlight, która zastąpiłaby zbudowaną wiele lat temu aplikację javascript, w której mam wiele interfejsów Javascript, które wywołują tę samą metodę Silverlight [ScriptableMember].

Wszystkie te starsze interfejsy JS muszą być obsługiwane, a także nowy interfejs do uzyskiwania dostępu do nowych funkcji, więc coś, co automatycznie konfiguruje interfejs JS i "deleguje" wywołanie właściwej metody Silverlight, pomogłoby znacznie przyspieszyć pracę.

Nie mogę tutaj wpisać kodu, więc to jest podsumowanie.

Author: Community, 2009-07-14

3 answers

Szczerze mówiąc, jeśli nie znasz typu w czasie kompilacji, nie ma ogromnej korzyści w tworzeniu Delegate. Nie chcesz używać DynamicInvoke; będzie to tak powolne, jak odbicie. Głównym wyjątkiem od tego jest sytuacja, gdy w cieniu czai się delegat typu, na przykład podczas subskrybowania zdarzenia-w takim przypadku EventInfo udostępnia to.

Dla informacji, w. NET 3.5 na Expression, jest:

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

To może pomóc w pewnym stopniu:

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; }
}
 22
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-14 10:43:07

Jeśli nie znasz z góry liczby lub typu parametrów, prawdopodobnie oznacza to, że nie znasz typu delegata, który chcesz utworzyć?

Jeśli o to chodzi, to utknąłeś w całkowicie ogólnej sprawie.

Jednak w większości typowych przypadków (brak parametrów ref/out, mało parametrów pozwalających na użycie jednego z istniejących typów) można było uciec od jednego z delegatów Func lub Action. (. NET 4.0 ma Func/Action typy dla ogromnej liczby parametrów, tak naprawdę musisz się tylko martwić o parametry out/ref.) Jeśli metoda ma zwracany typ non-void użyj Func, w przeciwnym razie użyj Action. Określ, jakiego typu użyć na podstawie liczby parametrów, np.

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

Użyj Type.MakeGenericType używając parametru typ I return type, aby uzyskać właściwy typ delegata, wtedy {[8] } powinno działać.

Nie mam teraz czasu, żeby popracować nad próbką, ale daj mi znać, jeśli będziesz chciała później.

Jedno pytanie: jak zamierzasz wykorzystać tego delegata? Coś innego będzie musiało wiedzieć, jak to wykonać...

 7
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-14 10:34:10

Dlaczego to skomplikowane?

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

[Uwaga: dodałem przedrostek tej metody " Create...". "Do..."jest mylące, ponieważ wprowadza cię w błąd, myśląc, że to nawrócenie.]

 6
Author: 0xbadf00d,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-28 08:43:02