2009-11-28 8 views
5

Fondamentalmente, sto accettando un nome evento come stringa, per ottenere il EventInfo. Quindi, sto scoprendo il tipo di gestore eventi e il tipo di argomento evento utilizzando la reflection, creando un nuovo delegato di quel tipo (myEventHandler) e collegandolo all'evento. Quando viene invocato lo , ho bisogno di downcast e passare gli argomenti al gestore.IL Emetti per il richiamo di un'istanza delegata?

Il mio codice è come di seguito. Il "gestore" deve essere invocato tramite myEventHandler, quando viene invocato mai "d". Ho bisogno di avere qualche codice di emettere Reflection lì dove ho messo ???. qualche idea?

EventHandler handler = delegate(object sender, EventArgs eventArgs) 
{ 
    //something will happen here         
}; 

Type[] typeArgs = { typeof(object), derivedEventArgsType }; 

DynamicMethod myEventHandler = new DynamicMethod("", typeof(void), typeArgs); 
var ilgen = myEventHandler.GetILGenerator(); 

//What should be the IL code here to 
//cast derviedEventArgs to EventArgs and 
//invoke the 'handler' above?????? 
ilgen.Emit(OpCodes.Pop); 
ilgen.Emit(OpCodes.Ret); 



Delegate d = dynamic.CreateDelegate(derviedEventHandlerType); 

//addMethod is the add MethodInfo for an Event 
addMethod.Invoke(target, new object[] { d }); 

Edit: Sulla base di osservazioni via riflettore.

Il riflettore codice generato per uno scenario in codice manualmente è

.method public hidebysig instance void <Main>b__1(object sender, class ConsoleApplication2.MyEventArgs e) cil managed 
{ 
    .maxstack 8 
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld class [mscorlib]System.EventHandler ConsoleApplication2.Program/<>c__DisplayClass3::handler 
    L_0007: ldarg.1 
    L_0008: ldarg.2 
    L_0009: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, class [mscorlib]System.EventArgs) 
    L_000e: nop 
    L_000f: ret 
} 

E questo è quello che ho provato sulla base di questo.

ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Ldarg_0); 
ilgen.Emit(OpCodes.Ldfld,eh.GetType().GetField("handler")); 
ilgen.Emit(OpCodes.Ldarg_1); 
ilgen.Emit(OpCodes.Ldarg_2); 
ilgen.EmitCall(OpCodes.Callvirt,eh.handler.Method, 
       new Type[]{ typeof(object), typeof(EventArgs) }); 
ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Ret); 

Ma questo sta causando un errore di tempo di esecuzione:

'Calling convention must be varargs'

Probabilmente mi manca qualcosa, hanno bisogno di avere un aspetto migliore in IL.

+2

Il trucco qui è sempre semplicemente scrivere il codice desiderato in C# e usare reflector/ILDASM per guardare l'IL. Vorrei indovinare una combinazione di ld, castclass e callvirt –

+0

Sì concordato. Prenderò questa strada, ma ho pensato che qualsiasi Reflection emettere Ninjas in SO potrebbe rapidamente farlo notare – amazedsaint

+0

Guardando ancora - dove risiede il "gestore"? rispetto agli arg? Sto pensando che sarà un dolore riunire i due. Sembra che la versione C# usi una classe di acquisizione, ma il tuo metodo dinamico al minuto è statico, quindi da nessuna parte spingere qualsiasi stato ... –

risposta

5

Si scopre che ho complicato molto le cose! Barry Kelly had the right idea:

static T CastDelegate<T>(Delegate src) 
    where T : class 
{ 
    return (T)(object)Delegate.CreateDelegate(
     typeof(T), 
     src.Target, 
     src.Method, 
     true); // throw on fail 
} 

che funziona per i miei casi di test.

5

OK: questo potrebbe essere d'aiuto; genera l'IL per passare da un tipo di delegato, a condizione che corrisponda al modello standard. Aggiunge un cast cast solo quando necessario (quindi se stai passando da un MouseEventArgs a uno EventArgs non è necessario, ma nella direzione opposta lo è). Dato che stai chiaramente lavorando con la riflessione, non ho usato i generici (il che renderebbe le cose più difficili).

Il bit sfacciato è che invece di utilizzare una cattura classe , finge il metodo appartiene ai dati avrei catturare, e utilizza lo stato come arg0. Non riesco a decidere se questo lo rende malvagio o intelligente, quindi vado con "clevil".

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Threading; 
using System.Windows.Forms; 

class Program { 
    static ParameterInfo[] VerifyStandardHandler(Type type) { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (!typeof(Delegate).IsAssignableFrom(type)) throw new InvalidOperationException(); 
     MethodInfo sig = type.GetMethod("Invoke"); 
     if (sig.ReturnType != typeof(void)) throw new InvalidOperationException(); 
     ParameterInfo[] args = sig.GetParameters(); 
     if (args.Length != 2 || args[0].ParameterType != typeof(object)) throw new InvalidOperationException(); 
     if (!typeof(EventArgs).IsAssignableFrom(args[1].ParameterType)) throw new InvalidOperationException(); 
     return args; 
    } 
    static int methodIndex; 
    static Delegate Wrap(Delegate value, Type type) { 
     ParameterInfo[] destArgs = VerifyStandardHandler(type); 
     if (value == null) return null; // trivial 
     if (value.GetType() == type) return value; // already OK 
     ParameterInfo[] sourceArgs = VerifyStandardHandler(value.GetType()); 
     string name = "_wrap" + Interlocked.Increment(ref methodIndex); 
     Type[] paramTypes = new Type[destArgs.Length + 1]; 
     paramTypes[0] = value.GetType(); 
     for (int i = 0; i < destArgs.Length; i++) { 
      paramTypes[i + 1] = destArgs[i].ParameterType; 
     } 
     DynamicMethod dyn = new DynamicMethod(name, null, paramTypes); 
     MethodInfo invoker = paramTypes[0].GetMethod("Invoke"); 
     ILGenerator il = dyn.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Ldarg_2); 
     if (!sourceArgs[1].ParameterType.IsAssignableFrom(destArgs[1].ParameterType)) { 
      il.Emit(OpCodes.Castclass, sourceArgs[1].ParameterType); 
     } 
     il.Emit(OpCodes.Call, invoker); 
     il.Emit(OpCodes.Ret); 
     return dyn.CreateDelegate(type, value); 
    } 
    static void Main() { 
     EventHandler handler = delegate(object sender, EventArgs eventArgs) { 
      Console.WriteLine(eventArgs.GetType().Name); 
     }; 
     MouseEventHandler wrapper = (MouseEventHandler)Wrap(handler, typeof(MouseEventHandler)); 
     MouseEventArgs ma = new MouseEventArgs(MouseButtons.Left, 1, 1, 1, 1); 
     wrapper(new object(), ma); 

     EventHandler backAgain = (EventHandler)Wrap(wrapper, typeof(EventHandler)); 
     backAgain(new object(), ma); 
    } 
} 

Ovviamente è ancora bisogno di generare un delegato all'evento utilizzando metodi regolari (Delegate.CreateDelegate, ecc), ma è possibile poi avvolgerla in un EventHandler, o viceversa.

+0

Impressionante. Devo ancora provare questo, ma non sono sicuro di poter ottenere una risposta migliore. Quindi, accettando questo come risposta. Grazie :) – amazedsaint

+0

Bene, confermato. Risolto quello che volevo. – amazedsaint

Problemi correlati