本文指出 You Can’t Unsubscribe from an Event Using a Lambda Expression .
例如您可以通过以下方式订阅:
d.Barked += (s, e) => Console.WriteLine("Bark: {0}", e);
但是你不能这样取消订阅:
d.Barked -= (s, e) => Console.WriteLine("Bark: {0}", e);
为什么?这与取消订阅委托(delegate)有什么区别,例如
EventHandler<string> handler = (s, e) => Console.WriteLine("Bark: {0}", e);
d.Barked += handler;
// ...
d.Barked -= handler;
最佳答案
这一切都归结为:出于委托(delegate)加法/减法的目的,两个委托(delegate)何时被视为相同。当您取消订阅时,它实际上是在使用 Delegate.Remove
中的逻辑,如果 .Target
和 .Method
都认为两个委托(delegate)是等效的匹配(至少,对于具有单个目标方法的委托(delegate)的简单情况;多播描述起来更复杂)。那么:lambda 上的 .Method
和 .Target
是什么(假设我们将其编译为 delegate,而不是 表达式)?
编译器实际上在这里有很大的自由度,但是发生的是:
- 如果 lambda 包含参数或变量的闭包,编译器会在编译器生成的类上创建一个方法(方法)来表示捕获上下文(也可以包含
这个
token ); 目标 是对此捕获上下文实例的引用(将由捕获范围定义) - 如果 lambda 不包含对参数或变量的闭包,但确实通过
this
(隐式或显式)使用每个实例的状态,则编译器会创建一个实例方法( 方法) 当前类型; 目标 是当前实例 (this
) - 否则编译器会创建一个静态方法(方法),并且目标为空(顺便说一句,在这种情况下,它还包含一个漂亮的字段来缓存单个静态委托(delegate)实例 - 所以在这种情况下,每个 lambda 只创建一个委托(delegate))
然而,它没有做的是比较许多具有相似主体的 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使用;第二种方法由第二个 lambda 使用。所以最终,它不起作用的原因是 .Method
不匹配。
在常规的 C# 术语中,它就像做:
obj.SomeEvent += MethodOne;
obj.SomeEvent -= MethodTwo;
其中 MethodOne
和 MethodTwo
里面有相同的代码;它不会退订任何内容。
如果编译器发现这一点,它可能很好,但不需要,因此不选择<会更安全/em> 到 - 这可能意味着不同的编译器开始产生非常不同的结果。
作为旁注;如果它确实尝试删除重复数据,那可能会非常困惑,因为您还会遇到捕获上下文的问题 - 那么在某些情况下它会“有效”,而在其他情况下则无效- 不清楚是哪一种 - 可能是最糟糕的情况。
关于c# - 为什么我不能使用 Lambda 表达式取消订阅事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25563518/