2011-03-04 29 views
3

我想了解动态生成的事件处理程序,我有困难,试图重新这种简单的情况有点:C#不能动态生成这个简单的事件处理程序

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

我用反射器产生IL为DoSomething的方法,它给我:

.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之后的例子中,被卡住了,然后试着将事情放松一个简单的例子,我可以工作。

+0

您可以使用表达式来编译函数。这通常比使用Reflection更容易.Emit – CodesInChaos 2011-03-04 12:32:09

+0

为什么你让你的“发射”变量是一个委托?使其与您的eventHandlerType和强制类型相同。然后你可以像调用普通而不是DynamicInvoke()那样调用它。如果这不是你的问题,哪一行会引发异常?该信息会有帮助。 – jonathanpeppers 2011-03-04 13:00:47

+0

即使使用您的EDIT,仍然不太可能需要通过在运行时生成IL来动态生成方法。您仍然可以在运行时使用反射类型和方法并使用['MethodBase.Invoke()'](http://msdn.microsoft.com/zh-cn/library/a89hcwhh%28v=vs.85% 29.aspx)来调用它们,或者您可以使用['dynamic'](http://msdn.microsoft.com/en-us/library/dd264741.aspx)类型。 – Timwi 2011-03-04 20:53:19

回答

5

不清楚为什么要动态创建此方法。我真的不能相信的,你不能只应用拉姆达到该活动的情况:

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

替换“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); 
} 
+0

谢谢,我还没有意识到DynamicMethod只能生成静态方法(尽管事后看来这很有意义)。它还解释了为什么反射器显示额外的参数,因为我的示例代码中的方法不是静态的。 – Akash 2011-03-04 13:54:36

+0

请参阅原始问题中的编辑,了解我为什么要这么做 - 您说得对,在我发布的示例中,这是没有意义的。 – Akash 2011-03-04 13:55:22

+1

@阿卡什:我编辑了解决这个问题的答案。请注意:) – Timwi 2011-03-04 20:57:42

0

这个怎么样的情况?

你有方法对和事件,使得每对是MethodAsync和MethodCompleted。每个MethodCompleted具有不同的签名(不同的eventargs的子类型,这是第二个参数)。

你想要创建一个包装器来调用一个给定的MethodAsync,它将一个通用的事件处理器挂接到相应的MethodCompleted。

通常情况下,你可以创建一个方法,“无效GlobalHandler(对象,对象)”,并做

MethodCompleted += GlobalHandler; 

但是,你不能传递事件对象,所以你必须使用反射来获得一个参考事件处理程序,然后执行AddHandler。同样,问题在于AddHandler不喜欢多态(看起来),并且抱怨说MethodCompleted想要的不是GlobalHandler。

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

+0

您可以将GlobalHandler转换为所需的委托类型吗? http://code.logos.com/blog/2008/07/casting_delegates.html提供了一些代码来完成必要的转换。 – Akash 2011-03-16 14:12:17

相关问题