IL Emit用于调用委托实例?

基本上,我接受一个事件名称作为字符串,以获取EventInfo 。 然后,我使用reflection发现事件处理程序类型和事件参数类型,创建该类型的新委托( myEventHandler ),并将其与事件挂钩。 当调用myEventHandler ,我需要向下转换并将参数传递给处理程序。

我的代码如下。 当调用’d’时,需要通过myEventHandler调用’handler’。 我需要在那里放一些reflection发射代码??? 有什么想法吗?

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

编辑:基于通过Reflector的观察。

reflection器为手动编码的场景生成的代码是

 .method public hidebysig instance void 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 } 

这就是我在此基础上尝试的。

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

但这会导致运行时错误:

‘召集会议必须是varargs’

可能我错过了什么,需要更好地了解IL。

事实certificate,我的事情过于复杂! 巴里凯利有正确的想法 :

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

这适用于我的测试用例。

好的 – 这可能会有所帮助; 它生成IL以在委托类型之间切换,只要它们与标准模式匹配即可。 它只在必要时才添加一个castclass(所以如果你从MouseEventArgs转到EventArgs则没有必要,但是反方向)。 由于你显然正在使用reflection,我没有使用generics(这会使事情变得更难)。

厚颜无耻的是,它不是使用捕获类,而是假装该方法属于我将捕获的数据,并将状态用作arg0 。 我无法判断这是否使它变得邪恶或聪明,所以我会选择“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); } } 

显然,你仍然需要使用常规方法( Delegate.CreateDelegate等)生成事件的Delegate.CreateDelegate ,但是你可以将它包装到EventHandler ,反之亦然。