Aka*_*ash 3 c# il reflection.emit
我正在尝试学习一些关于动态生成事件处理程序的知识,并且我很难尝试重新创建这种简单的情况:
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);
}
Run Code Online (Sandbox Code Playgroud)
我用反射器为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
}
Run Code Online (Sandbox Code Playgroud)
所以,我编写了以下代码来动态生成并执行一个等同于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");
}
Run Code Online (Sandbox Code Playgroud)
但是,当我运行它时,我得到一个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);
Run Code Online (Sandbox Code Playgroud)
也就是说,代码不起作用的原因是因为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");
}
Run Code Online (Sandbox Code Playgroud)
将" MyClass" 替换为您班级的名称.
关于问题的编辑,您不需要通过编写IL代码来生成动态方法,以便在运行时动态调用方法.只需使用Reflection,例如:
public void DoSomething(object a, object b)
{
var method = GetType().GetMethod("DoSomethingElse", BindingFlags.Instance | BindingFlags.Public);
method.Invoke(this, new object[] { a, b });
}
Run Code Online (Sandbox Code Playgroud)
要么:
// 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);
}
Run Code Online (Sandbox Code Playgroud)