我正在尝试学习一些有关动态生成事件处理程序的知识,但我在尝试重新创建这种简单情况时遇到了困难:
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 的例子,卡住了,然后尝试将事情展开为一个我可以开始工作的简单示例。
最佳答案
不清楚为什么要动态创建此方法。我真的想不出有什么情况不能将 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_0
和 Ldarg_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");
}
将“MyClass
”替换为您的类(class)名称。
关于您问题的编辑,您不需要通过编写 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);
}
关于C# 无法动态生成这个简单的事件处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5193714/