2009-11-28 9 views
5

Básicamente, estoy aceptando un nombre de evento como una cadena, para obtener el EventInfo. Luego, descubro el tipo de controlador de evento y tipo de argumento de evento usando reflection, creando un nuevo delegado de ese tipo (myEventHandler) y conectándolo con el evento. Cuando alguna vez se invoque myEventHandler, necesito abatir y pasar los argumentos al controlador.IL Emitir para invocar una instancia de delegado?

Mi código es el siguiente. El 'controlador' debe invocarse a través del myEventHandler, siempre que se invoque 'd'. Necesito tener algún código de emisión de Reflexión allí donde puse ??? ¿Alguna 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 }); 

Editar: Basándose en las observaciones a través del reflector.

El reflector código generado para un escenario codificado manualmente es

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

Y esto es lo que he intentado en base a eso.

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

Pero esto está causando un error de tiempo de ejecución:

'Calling convention must be varargs'

Probablemente me falta algo, necesita tener una mejor visión en IL.

+2

El truco aquí siempre es simplemente escribir el código que desea en C# y usar el reflector/ILDASM para ver el IL. Supongo que una combinación de ld, castclass y callvirt –

+0

Sí estuvo de acuerdo. Tomaré esa ruta, pero pensé que cualquier Reflejo emitir Ninjas en SO podría señalarlo rápidamente – amazedsaint

+0

Mirando de nuevo, ¿dónde reside el "manejador"? relativo a los args? Estoy pensando que va a ser un dolor juntar los dos. Parece que la versión C# usa una clase de captura, pero su método dinámico en el momento es estático, por lo que no hay forma de empujar ningún estado ... –

Respuesta

5

¡Resulta que estaba complicando demasiado las cosas! 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 
} 

que funcione para mis casos de prueba.

5

OK - esto podría ayudar; genera el IL para alternar entre tipos de delegados, siempre que coincidan con el patrón estándar. Agrega una clase de castc solo cuando es necesario (por lo que si va de MouseEventArgs a EventArgs no es necesario, sino en la dirección inversa). Como claramente está trabajando con la reflexión, no he usado genéricos (lo que haría las cosas más difíciles).

El poco descarado es que en lugar de utilizar una captura clase , que pretende el método pertenece a los datos que capturaría, y utiliza el Estado como arg0. No puedo decidir si eso lo hace malvado o inteligente, así que iré 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); 
    } 
} 

Obviamente usted todavía tiene que generar un delegado al evento usando métodos regulares (Delegate.CreateDelegate etc.), pero, a continuación, se puede envolver a un EventHandler, o al revés.

+0

Impresionante. Todavía necesito probar esto, pero no estoy seguro de si puedo obtener una mejor respuesta. Por lo tanto, aceptando esto como una respuesta. Gracias :) – amazedsaint

+0

Bueno, confirmado. Resolvió lo que yo quería. – amazedsaint

Cuestiones relacionadas