2009-07-14 21 views
32

Dopo googling e atterraggio su SO e dopo aver letto this other questionCostruisce un delegato da MethodInfo?

E 'possibile costruire una corretta delegato da un MethodInfo se non sapevi il numero o il tipo di parametri in fase di compilazione?

Ulteriori informazioni su questo: questo può essere fatto elegantemente senza l'uso di Reflection.Emit o tipo di builder?

Questo è un po 'un peccato per me perché Delegate.CreateDelegate mi richiede di specificare il tipo di Delegato corretto come il primo parametro altrimenti genera eccezioni o invoca un metodo errato.

Sto costruendo alcuni ingranaggi ninja e questo sarebbe di grande aiuto ... Grazie!


Ecco una soluzione generica:

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

Nota: Sto costruendo un'applicazione Silverlight che dovrebbe sostituire un'applicazione javascript anni incorporati fa, in cui ho più Interfacce Javascript che chiamano nello stesso metodo [ScriptableMember] di Silverlight.

Tutte quelle legacy interfacce JS hanno bisogno di essere supportati proprio come la nuova interfaccia per l'accesso a nuove funzioni, in modo da qualcosa che messe a punto automaticamente l'interfaccia JS e "delegati" la chiamata al metodo Silverlight giusta sarebbe aiuta a velocizzare il lavoro molto.

Non riesco a inserire il codice qui, quindi questo è il riassunto.

risposta

22

Per essere onesti, se non si conosce il tipo in fase di compilazione, non c'è un enorme vantaggio nella creazione di Delegate. Non si desidera utilizzare DynamicInvoke; sarà lento come la riflessione. L'eccezione principale a questo è quando c'è un tipo di delega in agguato nell'ombra, ad esempio quando si sottoscrive un evento - nel qual caso EventInfo lo rende disponibile.

Per info, in .NET 3.5 sul Expression, c'è:

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

che potrebbero contribuire in misura:

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

Lo sto creando per incollare Silverlight [ScriptableMember] e un'interfaccia JavaScript separata, quindi non devo preoccuparmi di mantenere sincronizzate le firme dei metodi in entrambe le posizioni. – chakrit

+0

Wow .... questo ha aiutato molto! Ragazzi, rock! – chakrit

+0

@ Marc Gravell, non sono in grado di richiamare il delegato creato nel codice precedente come d(). Dopo aver cercato su google ho scoperto che dynamicInvoke può essere usato per invocare il metodo, che è molto lento. aiuto di pls. Sono nuovo per delegati ed eventi. il mio requisito è di invocare un metodo in modo dinamico, il numero o il tipo di parametri saranno noti solo in fase di esecuzione – Saranya

7

Se non si conosce il numero o il tipo di parametri in anticipo, presumibilmente ciò significa che non si conosce il tipo di delegato che si desidera creare sia?

Se questo è il caso, sei bloccato nel caso assolutamente generale.

Tuttavia, per la maggior parte casi comuni (nessun ref/out parametri, alcuni parametri sufficiente per utilizzare uno dei tipi esistenti) si potrebbe ottenere via con uno dei Func o Action delegati. (.NET 4.0 ha i tipi Func/Action per un numero enorme di parametri, quindi in realtà dovresti preoccuparti solo dei parametri out/ref.) Se il metodo ha un tipo di reso non vuoto usa Func, altrimenti usa Action. Calcola il tipo da utilizzare in base al numero di parametri, ad es.

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

Usa Type.MakeGenericType utilizzando i tipi di parametri e il tipo per ottenere il tipo delegato diritto di recesso, quindi Delegate.CreateDelegate dovrebbe funzionare.

Non ho tempo di elaborare un esempio in questo momento, ma fammi sapere se vuoi che lo faccia in seguito.

Una domanda: come intendete usare questo delegato? Qualcos'altro avrà bisogno di sapere come eseguirlo, sicuramente ...

+0

ah-ha ... buona visione del MakeGenericType + Func ... lo farei :-) – chakrit

+4

Per evitare il tuo Tipo statico [], considera Expression.GetActionType/Expression.GetFuncType - vedi post. Vorrei ** sperare ** che questi metodi siano stati estesi per includere le nuove varianti di .NET 4.0. –

+0

Ho aggiunto il "perché" alla domanda – chakrit

6

Perché così complicato?

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 a margine: ho aggiunto questo metodo "Crea ...". "Per ..." è confusionario in quanto ti induce a pensare che si tratti di una conversione.]

+0

Il metodo 'Expression.GetDelegateType' è in realtà .NET 4 e SL 4 specifici. La mia domanda è stata posta prima della versione .NET4 e SL4. Comunque grazie per la risposta. Tieni presente che dovrai associare un delegato a una destinazione se il metodo è un metodo di istanza, in modo che la parte vincolante sia ancora obbligatoria. – chakrit

+0

Passando un oggetto come secondo parametro (chiamato 'firstArgument' - Sto passando null qui) è possibile specificare l'oggetto a cui è associato il Delegato. O mi manca il punto? – 0xbadf00d

Problemi correlati