代码之家  ›  专栏  ›  技术社区  ›  amazedsaint

调用委托实例时发出IL?

  •  6
  • amazedsaint  · 技术社区  · 14 年前

    基本上,我接受一个事件名作为字符串,以获取 EventInfo . 然后,我将使用反射发现事件处理程序类型和事件参数类型,并创建该类型的新委托。( myEventHandler ,并将其与事件关联起来。什么时候 迈文登德勒 被调用时,我需要向下转换并将参数传递给处理程序。

    我的代码如下。需要通过调用“handler” 迈文登德勒 ,当调用“d”时。我需要让反射在我放的地方发出代码?你说什么?有什么想法吗?

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

    编辑: 根据反射镜的观察结果。

    对于手动编码的场景,反射器生成的代码是

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

    这就是我尝试的基础。

    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。

    2 回复  |  直到 10 年前
        1
  •  5
  •   Marc Gravell    14 年前

    事实证明,我已经完全忘记了复杂的事情!巴里凯利 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
    }
    

    这对我的测试用例有效。

        2
  •  5
  •   Marc Gravell    14 年前

    好的-这可能有帮助;它生成IL来在委托类型之间切换,只要它们符合标准模式。它只在必要时添加castclass(因此,如果您要从 MouseEventArgs EventArgs 这是不必要的,但在相反的方向上是必要的)。因为你显然是在思考,所以我没有使用泛型(这会使事情变得更困难)。

    厚颜无耻的一点是,不要用 捕获 类,它假装该方法属于我要捕获的数据,并将状态用作 arg0 .我不能决定这是邪恶还是聪明,所以我会选择“克莱维尔”。

    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 但是你可以把它包装成 EventHandler 或者反过来。