2014-08-29 60 views
10

该文章指出You Can’t Unsubscribe from an Event Using a Lambda Expression为什么我不能使用Lambda表达式取消订阅事件?

E.g.您可以订阅如下:

d.Barked += (s, e) => Console.WriteLine("Bark: {0}", e); 

,但你不能退订这样的:

d.Barked -= (s, e) => Console.WriteLine("Bark: {0}", e); 

为什么?这与退订代理人有什么区别,例如

EventHandler<string> handler = (s, e) => Console.WriteLine("Bark: {0}", e); 
d.Barked += handler; 

// ... 

d.Barked -= handler; 
+0

http://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c-sharp – 2014-08-29 07:27:38

+0

是的,这与文章中的内容大致相同,但它不能解释为什么。 – DaveDev 2014-08-29 07:28:22

+0

@TimSchmelter不幸的是我的浏览器只能看到过去。 – DaveDev 2014-08-29 07:38:55

回答

32

这一切都归结为:什么时候两个代表为了委托加/减的目的考虑相同。当取消订阅时,它基本上使用从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; 

其中MethodOneMethodTwo里面有他们相同的代码;它不会取消订阅任何内容。

这可能是不错如果编译器看准了这一点,但它并不需要,因此它是安全的,它不 - 它可能意味着不同的编译器开始生产非常不同的结果。

作为一个侧面说明;如果确实试图去重复,可能会非常令人困惑,因为您还会遇到捕获上下文的问题 - 那么在某些情况下它会“工作”而不是其他问题 - 并不明显 - 可能是最糟糕的情况。

+0

我觉得这是令人伤心的讽刺,我仍然是这里唯一的上流人。 (在我看来,这几乎同样具有讽刺意味 - 这也回答了“如何”更合适;感谢修复我的事件清除心理模型。) – sehe 2014-08-29 08:46:41

+0

优秀的解释!自从我在C#理解中发现一个缺口已经有一段时间了,但今天我有了一个新的见解。 我需要理解这一点,因为我想使用匿名方法(Action类型)的引用值来确定它是否已被注册为回调。现在,我知道只有在没有关闭并且可能会导致意想不到的问题时才会起作用。 – 2015-05-11 15:23:08

相关问题