这一切都归结为:什么时候两个代表为了委托加/减的目的考虑相同。当取消订阅时,它基本上使用从Delegate.Remove
逻辑,它参考等效两位代表如果两个.Target
和.Method
匹配(至少,对于具有单一的目标方法的委托的简单情况;多播是更复杂的描述) 。那么:lambda上的.Method
和.Target
是什么(假设我们将它编译为代表,而不是表达式)?
编译器实际上有很大的自由在这里,但什么发生是:
- 如果拉姆达包括通过参数或变量封闭,编译器创建一个方法(方法)代表该捕获上下文(其也可以包括
this
令牌)一个编译器生成的类; 目标是对此捕获上下文实例的引用(将由捕获范围定义)
- 如果lambda不包含通过参数或变量的闭包,但确实使用每实例状态经由
this
(隐式或显式),编译器创建在当前类型的实例方法(该方法);所述目标是当前实例(this
)
- 否则编译器创建一个静态方法(方法),并且目标为空(顺便说一下,在这种情况下它还包括一个漂亮的字段来缓存单一的静态委托实例 - 所以在这种情况下,只有一个委托曾经每拉姆达创建)
它所不做,不过,与外观类似的机构,以减少任何比较大量的lambda表达式。所以,我所得到的,当我编译你的代码是静态方法:
[CompilerGenerated]
private static void <Main>b__0(object s, string e)
{
Console.WriteLine("Bark: {0}", e);
}
[CompilerGenerated]
private static void <Main>b__2(object s, string e)
{
Console.WriteLine("Bark: {0}", e);
}
(这里的Main
只是因为在我的测试台的lambda表达式是Main
法里 - 但最终的编译器可以选择任何不可发音名称它选择在这里)
第一种方法是使用由第一拉姆达;第二种方法由第二个lambda使用。所以最终,它不起作用的原因是因为.Method
不匹配。
在普通的C#而言,它会像做:
obj.SomeEvent += MethodOne;
obj.SomeEvent -= MethodTwo;
其中MethodOne
和MethodTwo
里面有他们相同的代码;它不会取消订阅任何内容。
这可能是不错如果编译器看准了这一点,但它并不需要,因此它是安全的,它不选 - 它可能意味着不同的编译器开始生产非常不同的结果。
作为一个侧面说明;如果确实试图去重复,可能会非常令人困惑,因为您还会遇到捕获上下文的问题 - 那么在某些情况下它会“工作”而不是其他问题 - 并不明显 - 可能是最糟糕的情况。
http://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c-sharp – 2014-08-29 07:27:38
是的,这与文章中的内容大致相同,但它不能解释为什么。 – DaveDev 2014-08-29 07:28:22
@TimSchmelter不幸的是我的浏览器只能看到过去。 – DaveDev 2014-08-29 07:38:55