2009-11-28 4 views
5

Fondamentalement, j'accepte un nom d'événement sous forme de chaîne, pour obtenir le EventInfo. Ensuite, je découvre le type de gestionnaire d'événement et le type d'argument d'événement en utilisant la réflexion, en créant un nouveau délégué de ce type (myEventHandler), et en le reliant à l'événement. Quand jamais myEventHandler est invoqué, j'ai besoin de downcast et passer les arguments au gestionnaire.IL Emettre pour appeler une instance de délégué?

Mon code est le suivant. Le 'gestionnaire' doit être appelé via myEventHandler, quand jamais 'd' est invoqué. Je dois avoir un code d'émission de réflexion là où je mets ??? Des pensées?

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: Sur la base des observations via réflecteur.

Le réflecteur code généré pour un scénario codé manuellement est

.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 
} 

Et ce que j'ai essayé sur cette base.

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

Mais cela provoque une erreur de temps d'exécution:

'Calling convention must be varargs'

Probablement je manque quelque chose, besoin d'avoir un meilleur regard sur IL.

+2

L'astuce ici est toujours simplement d'écrire le code que vous voulez en C# et d'utiliser réflecteur/ILDASM pour regarder l'IL. Je devinerais une combinaison de ld, castclass, et callvirt –

+0

Yep convenu. Je vais prendre cette route, mais je pense que toute Réflexion émise Ninjas dans SO peut rapidement le signaler – amazedsaint

+0

En regardant à nouveau - où réside "gestionnaire"? par rapport aux args? Je pense que ça va être difficile de réunir les deux. Il semble que la version C# utilise une classe de capture, mais votre méthode dynamique à la minute est statique, donc nulle part pour pousser n'importe quel état ... –

Répondre

5

Il s'avère que je compliquais énormément les choses! 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 
} 

qui fonctionne pour mes cas de test.

5

OK - cela pourrait aider; il génère l'IL pour basculer entre les types de délégué, tant qu'ils correspondent au modèle standard. Il ajoute une classe de cast uniquement quand nécessaire (donc si vous passez d'un MouseEventArgs à un EventArgs ce n'est pas nécessaire, mais dans le sens inverse c'est). Puisque vous travaillez clairement sur la réflexion, je n'ai pas utilisé de génériques (ce qui rendrait les choses plus difficiles).

Le peu culotté est qu'au lieu d'utiliser une capture classe, il feint la méthode appartient aux données je capturer, et utilise l'état comme arg0. Je ne peux pas décider si cela rend mal ou malin, alors je vais aller avec "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); 
    } 
} 

Il est évident que vous devez toujours générer un délégué à l'événement en utilisant des méthodes régulières (Delegate.CreateDelegate etc), mais vous pouvez ensuite l'envelopper à un EventHandler, ou l'inverse.

+0

Awesome. Encore besoin d'essayer, mais je ne suis pas sûr si je peux obtenir une meilleure réponse. Par conséquent, en acceptant cela comme une réponse. Merci :) – amazedsaint

+0

Eh bien, confirmé. J'ai résolu ce que je voulais. – amazedsaint

Questions connexes