2009-07-14 8 views
32

Po googlowania i lądowania na SO i po przeczytaniu this other questionBuduje delegata z MethodInfo?

Czy możliwe jest zbudowanie prawidłowego Delegata z MethodInfo jeśli nie wiesz ilości i typów parametrów w czasie kompilacji?

Więcej na ten temat: czy można to zrobić elegancko, bez użycia Reflection.Emit lub typu budowniczych?

Jest to dla mnie trochę kłopotliwe, ponieważ Delegate.CreateDelegate wymaga od mnie określenia właściwego typu Delegata jako pierwszego parametru, inaczej wyrzuciłby wyjątki lub wywołałby niepoprawną metodę.

Buduję trochę narzędzi ninja, a to by bardzo pomogło ... Dzięki!


Oto generic 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; 
} 

Uwaga: Buduję aplikacji Silverlight, która zastąpi wbudowaną lat-temu javascript aplikację, w której mam wiele Interfejsy JavaScript wywołujące tę samą metodę Silverlight [ScriptableMember].

Wszystkie starsze interfejsy JS muszą być obsługiwane, a także nowy interfejs dostępu do nowych funkcji, więc coś, co automatycznie konfiguruje interfejs JS i "deleguje" połączenie do odpowiedniej metody Silverlight, znacznie przyspieszy pracę.

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

Odpowiedz

22

Szczerze mówiąc, jeśli nie znasz typu podczas kompilacji, nie ma ogromnej korzyści w tworzeniu Delegate. Nie chcesz używać DynamicInvoke; będzie to mniej więcej tak samo powolne jak odbicie. Głównym wyjątkiem jest sytuacja, w której w cieniu czai się typ uczestnika, na przykład przy subskrybowaniu wydarzenia - w takim przypadku udostępnienie go jest możliwe.

Dla informacji, w .NET 3.5 w Expression istnieje:

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

To może pomóc w 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; } 
} 
+1

Buduję to, aby skleić razem Silverlight [ScriptableMember] i osobny interfejs JavaScript, więc nie muszę się martwić o synchronizowanie sygnatur metod w obu lokalizacjach. – chakrit

+0

Wow .... to bardzo pomogło! Jesteście rocka! – chakrit

+0

@ Marc Gravell, nie jestem w stanie wywołać delegata utworzonego w powyższym kodzie jak d(). Po uruchomieniu go, stwierdziłem, że można użyć metody dynamicInvoke do wywołania metody, która jest bardzo powolna. pls pomoc. Jestem nowy dla delegatów i wydarzeń. moim wymaganiem jest wywołanie metody dynamicznie, liczba lub typ parametrów będzie znana w środowisku wykonawczym tylko – Saranya

7

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

Jeśli tak jest, utknąłeś w absolutnie ogólnym przypadku.

Jednak dla większości wspólnych przypadkach (brak Ref/out parametry wystarczającej kilka parametrów użyć jednego z istniejących typów) można uciec z jednego z Func lub Action delegatów. (.NET 4.0 ma typy Func/Action dla ogromnej liczby parametrów, więc naprawdę musisz się tylko martwić o parametry out/ref.) Jeśli metoda ma non-void return type, użyj Func, w przeciwnym razie użyj Action. Sprawdź, jakiego typu użyć na podstawie liczby parametrów, np.

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

Zastosowanie Type.MakeGenericType używając typów parametrów i typu, aby uzyskać odpowiedni typ delegata powrotu, a następnie Delegate.CreateDelegate powinno działać.

Nie mam czasu, aby opracować próbkę już teraz, ale daj mi znać, jeśli chcesz mnie później.

Jedno pytanie: w jaki sposób zamierzasz korzystać z tego delegata? Coś innego będzie musiało wiedzieć, jak go wykonać, z pewnością ...

+0

ah-ha ... dobre podejście do MakeGenericType + Func ... to by to zrobiło :-) – chakrit

+4

Aby uniknąć static Type [], weź pod uwagę wyrażenie Expression.GetActionType/Expression.GetFuncType - patrz post. Mam ** nadzieję, ** że te metody zostały rozszerzone o nowe warianty .NET 4.0. –

+0

Dodałem "dlaczego" do pytania – chakrit

6

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

[Nota boczna: Podałem tę metodę "Utwórz ...". "To ..." jest mylące, ponieważ wprowadza cię w błąd, gdy myślisz, że to konwersja.]

+0

Metoda 'Expression.GetDelegateType' jest w rzeczywistości specyficzna dla .NET 4 i SL 4. Moje pytanie zadano przed wydaniem .NET4 i SL4. W każdym razie dzięki za odpowiedź. Pamiętaj, że musisz powiązać delegata z celem, jeśli metoda jest metodą instancji, więc część wiążąca jest nadal obowiązkowa. – chakrit

+0

Przekazując obiekt jako drugi parametr (nazywany "firstArgument" - tutaj przekazuję wartość null), można określić obiekt, do którego przypisany jest delegat. Czy może tęsknię za tym punktem? – 0xbadf00d

Powiązane problemy