2012-08-26 10 views
8

Voglio essere in grado di fare l'equivalente al seguente in fase di esecuzione:Come posso creare dinamicamente un'azione <T> in fase di runtime?

var action = new Action<ANYTHING AT RUNTIME>(obj => Console.WriteLine("Called = " + obj)); 

so che ho bisogno di ottenere il tipo corretto per l'azione, ma non è sicuro come ottenere il bit finale utilizzando Delegate.create . Type rappresentano T nella definizione Azione.

var actionType = typeof(Action<>).MakeGenericType(Type); 
var constructor = actionType.GetConstructors()[0]; 
var @delegate = Delegate.CreateDelegate(actionType, <WHAT GOES HERE>); 

punto la gente sembra essere mancante è che sto cercando di creare un'istanza di azione in cui T non può essere specificato in modo statico, perché viene utilizzato da una classe derivata da Attribute - questo significa T potrebbe essere qualsiasi cosa e non può essere definisce come definizione generica

Acclamazioni

+0

Quale parte del 'action = nuova azione (obj => Console.WriteLine ("chiamato =" + obj));' vuoi per generare dinamicamente? – Mark

+0

Voglio creare \ inizializzare il parametro 'azione' – AwkwardCoder

+0

Sono confuso. Non esiste _parameter_ chiamato 'action'. Cosa vuoi ottenere? – Mark

risposta

-1

La risposta è di creare un delegato MyActionDelegate e quindi utilizzare:

delegate void MyActionDelegate(T arg); 
Delegate @delegate = new MyActionDelegate((a) => Console.WriteLine(a)); 

Ecco un esempio di lavoro utilizzando una classe generica:

public class MyClass<T> 
{ 
    public delegate void ActionDelegate(T arg); 

    public void RunGenericAction(T arg) 
    { 
     var actionType = typeof(Action<>).MakeGenericType(typeof(T)); 
     var constructor = actionType.GetConstructors()[0]; 
     Delegate @delegate = new ActionDelegate((a) => { Console.WriteLine(arg); }); 
     var inst = (Action<T>)constructor.Invoke(new object[] { 
      @delegate.Target, 
      @delegate.Method.MethodHandle.GetFunctionPointer() 
     }); 
     inst(arg); 
    } 
} 

usare in questo modo, il quale emette 123 alla console:

var c = new MyClass<int>(); 
c.RunGenericAction(123); 

Noterete Sto passando due parametri per Constructor.Invoke; questo perché risulta che un argomento delegato compila effettivamente come due argomenti: l'oggetto target della funzione e un puntatore alla funzione. Non posso prendermi il merito per il gioco di fantasia lì; informazioni "prese in prestito" da this excellent answer on how to pass a delegate argument using reflection.

-1

Utilizzare il codice seguente per creare un delegato, che è in ritardo con il parametro type. Vedi anche How to: Examine and Instantiate Generic Types with Reflection.

abstract class ActionHelper { 

    protected abstract Delegate CreateActionImpl(); 

    // A subclass with a static type parameter 
    private class ActionHelper<T> : ActionHelper { 
     protected override Delegate CreateActionImpl() { 
      // create an Action<T> and downcast 
      return new Action<T>(obj => Console.WriteLine("Called = " + (object)obj)); 
     } 
    } 

    public static Delegate CreateAction(Type type) { 
     // create the type-specific type of the helper 
     var helperType = typeof(ActionHelper<>).MakeGenericType(type); 
     // create an instance of the helper 
     // and upcast to base class 
     var helper = (ActionHelper)Activator.CreateInstance(helperType); 
     // call base method 
     return helper.CreateActionImpl(); 
    } 
} 

// Usage 
// Note: The "var" is always "Delegate" 
var @delegate = ActionHelper.CreateAction(anyTypeAtRuntime); 

Detto questo, non è consigliabile utilizzare questo metodo. Invece, utilizzare

Action<object> action = obj => Console.WriteLine("Called = " + obj); 

Offre

  • la stessa funzionalità.

    Il codice originale "Called = " + obj" chiama .ToString() sul parametro. Così fa quanto sopra.

  • Nessuna differenza di prestazioni.

    Se il parametro obj è un tipo di valore, entrambe le varianti eseguono un'operazione di inscatolamento. Il pugilato nel primo non è ovvio, ma i tipi di valore di caselle "Called = " + obj".

  • Più breve e meno soggetto a errori.

+0

"Nessuna differenza di prestazioni" Per cosa? non hai fornito alcun codice che crea dinamicamente un delegato. Fornisci comodamente una classe astratta con un metodo astratto 'CreateActionImpl' ma non fornisci un'implementazione, l'unico metodo che creerebbe dinamicamente l'azione. –

+0

@Peter Ho spiegato le implicazioni di prestazione nel paragrafo che segue il reclamo. Inoltre, l'OP voleva _just il tipo parametro_ del delegato in ritardo. Che il codice fa senza Reflection.Emit. – Mark

0

È possibile utilizzare seguente codice, funziona se il tipo può essere colato a un oggetto:

Action<object> func = o => Console.WriteLine("Called = " + o.GetType().Name); 
var actionType = typeof(Action<>).MakeGenericType(type); 
var constructor = actionType.GetConstructors()[0]; 
var @delegate = Delegate.CreateDelegate(actionType, func.Method); 

Tuttavia se il tipo è enum o di altro tipo di valore non funzionerà.

1

Se si conosce l'operazione che è necessario eseguire e come eseguirla indipendentemente dal tipo (come nell'esempio), perché non creare un metodo generico che esegue l'operazione e creare il delegato in questo modo?

class Program 
{ 
    public static void Perform<T>(T value) 
    { 
     Console.WriteLine("Called = " + value); 
    } 

    public static Delegate CreateAction(Type type) 
    { 
     var methodInfo = typeof (Program).GetMethod("Perform").MakeGenericMethod(type); 
     var actionT = typeof (Action<>).MakeGenericType(type); 
     return Delegate.CreateDelegate(actionT, methodInfo); 
    } 

    static void Main(string[] args) 
    { 
     CreateAction(typeof (int)).DynamicInvoke(5); 
     Console.ReadLine(); 
    } 
} 
Problemi correlati