3

我正在尝试学习一些有关动态生成事件处理程序的知识,但在尝试重新创建这种简单情况时遇到了困难:

public delegate void SomethingHappenedEventHandler(object sender, object args);
public event SomethingHappenedEventHandler SomethingHappened;

// This is the event handler that I want to create dynamically
public void DoSomething(object a, object b)
{
    DoSomethingElse(a, b);
}

public void DoSomethingElse(object a, object b)
{
    Console.WriteLine("Yay! " + a + " " + b);
}

我使用反射器为 DoSomething 方法生成 IL,它给了我:

.method public hidebysig instance void DoSomething(object a, object b) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: ldarg.2 
    L_0003: call instance void MyNamespace::DoSomethingElse(object, object)
    L_0008: ret 
}

因此,我编写了以下代码来动态生成和执行等效于 DoSomething(...) 的方法:

public void CreateDynamicHandler()
{
    var eventInfo = GetType().GetEvent("SomethingHappened");
    var eventHandlerType = eventInfo.EventHandlerType;

    var dynamicMethod = new DynamicMethod("DynamicMethod", null, new[] { typeof(object), typeof(object) }, GetType());
    var ilgen = dynamicMethod.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Ldarg_2);

    MethodInfo doSomethingElse = GetType().GetMethod("DoSomethingElse", new[] { typeof(object), typeof(object) });
    ilgen.Emit(OpCodes.Call, doSomethingElse);
    ilgen.Emit(OpCodes.Ret);

    Delegate emitted = dynamicMethod.CreateDelegate(eventHandlerType);
    emitted.DynamicInvoke("hello", "world");
}

但是,当我运行它时,我得到一个 InvalidProgramException:JIT 编译器遇到内部限制。

谁能指出我哪里出错了?

[编辑] 正如一些人所评论的,如果我知道所涉及的所有类型,那么整个 IL 生成都是不必要的。我这样做的原因是,这是在运行时为我不知道所涉及的所有类型的事件动态生成事件处理程序的第一步。基本上,我一直在关注http://msdn.microsoft.com/en-us/library/ms228976.aspx上的示例,但被卡住了,然后尝试将事情展开为一个我可以开始工作的简单示例。

4

2 回答 2

5

不清楚为什么要动态创建此方法。我真的想不出任何不能将 lambda 应用于事件的情况:

public delegate void SomethingHappenedEventHandler(object sender, object args);
public event SomethingHappenedEventHandler SomethingHappened;

public void DoSomethingElse(object a, object b)
{
    Console.WriteLine("Yay! " + a + " " + b);
}

// If the signature exactly matches the delegate, just use the method name
SomethingHappened += DoSomethingElse;

public void DoSomethingDifferent(object a)
{
    Console.WriteLine("Yay! " + a);
}

// Otherwise, just use a lambda expression
SomethingHappened += (a, b) => DoSomethingDifferent(a);

也就是说,您的代码不起作用的原因是因为DynamicMethod只生成静态方法。因此,IL 代码是无效的,因为Ldarg_0Ldarg_1加载的两个参数却Ldarg_2引用了一个不存在的参数。如果我按以下方式更改它,它会按预期工作——它现在是一个具有三个参数的静态方法,其中第一个参数基本上是this

public void CreateDynamicHandler()
{
    var dynamicMethod = new DynamicMethod("DynamicMethod", null,
        new[] { typeof(MyClass), typeof(object), typeof(object) }, typeof(MyClass));
    var ilgen = dynamicMethod.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Ldarg_2);

    MethodInfo doSomethingElse = typeof(MyClass).GetMethod("DoSomethingElse",
        new[] { typeof(object), typeof(object) });
    ilgen.Emit(OpCodes.Call, doSomethingElse);
    ilgen.Emit(OpCodes.Ret);

    Delegate emitted = dynamicMethod.CreateDelegate(
        typeof(Action<MyClass, string, string>));
    emitted.DynamicInvoke(this, "Hello", "World");
}

将“<code>MyClass”替换为您的班级名称。

关于您的问题的编辑,您不需要通过编写 IL 代码来生成动态方法以便在运行时动态调用方法。只需使用反射,例如:

public void DoSomething(object a, object b)
{
    var method = GetType().GetMethod("DoSomethingElse", BindingFlags.Instance | BindingFlags.Public);
    method.Invoke(this, new object[] { a, b });
}

或者:

// Note “static”
public static void DoSomething(dynamic instance, object a, object b)
{
    // This will call whatever “DoSomethingElse” method exists on the type
    // that “instance” has *at run-time*
    instance.DoSomethingElse(a, b);
}
于 2011-03-04T13:16:43.343 回答
0

这个场景怎么样...

您有成对的方法和事件,每对都是 MethodAsync 和 MethodCompleted。每个 MethodCompleted 都有不同的签名(在 eventargs 的子类型中有所不同,这是第二个参数)。

您想要创建一个包装器来调用给定的 MethodAsync,它将通用事件处理程序与相应的 MethodCompleted 挂钩。

通常,您可以创建一个方法“void GlobalHandler(object, object)”,然后执行

MethodCompleted += GlobalHandler;  

但是,您不能传递事件对象,因此您必须使用反射来获取对事件处理程序的引用,然后执行 AddHandler。同样,问题是 AddHandler 不喜欢多态性(看起来),并抱怨 MethodCompleted 想要 GlobalHandler 以外的东西。

在这种情况下,您似乎需要创建 MethodCompleted 期望的 EXACT 签名的 DynamicMethod,然后从中调用 GlobalHandler。我是对的,还是我在这里也遗漏了一些东西。

于 2011-03-16T12:12:13.987 回答