【问题标题】:IL Emit for invoking a delegate instance?IL Emit 用于调用委托实例?
【发布时间】:2009-11-28 18:18:08
【问题描述】:

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

我的代码如下。当调用“d”时,需要通过myEventHandler 调用“处理程序”。我需要在放置 ??? 的地方放一些反射发射代码。有什么想法吗?

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

但这会导致运行时错误:

'调用约定必须是可变参数'

可能我遗漏了一些东西,需要更好地了解 IL。

【问题讨论】:

  • 这里的诀窍总是简单地用 C# 编写你想要的代码并使用反射器/ILDASM 来查看 IL。我猜想是 ld、castclass 和 callvirt 的组合
  • 是的,同意。我会走那条路,但认为 SO 中的任何反射发射忍者都可能很快指出这一点
  • 再看 - “处理程序”驻留在哪里?相对于参数?我认为将两者结合起来会很痛苦。看起来 C# 版本使用了一个捕获类,但是你现在的动态方法是静态的,所以无处可推任何状态......

标签: c# .net reflection reflection.emit


【解决方案1】:

事实证明我把事情复杂化了!巴里·凯利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】:

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

    厚颜无耻的一点是,它没有使用 capture 类,而是假装该方法属于我要捕获的数据,并将状态用作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 等)生成事件委托,但您可以将其包装为 EventHandler,或反过来。

    【讨论】:

    • 太棒了。仍然需要尝试一下,但我不确定我是否能得到更好的答案。因此,接受这个作为答案。谢谢:)
    • 嗯,确认。解决了我想要的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 2015-02-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多