简体   繁体   English

为什么我不能使用Lambda表达式取消订阅事件?

[英]Why can't I unsubscribe from an Event Using a Lambda Expression?

This article states You Can't Unsubscribe from an Event Using a Lambda Expression . 本文说明您无法使用Lambda表达式取消订阅事件

Eg you can subscribe as follows: 例如,您可以订阅如下:

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

but you can't unsubscribe like this: 但是你不能这样取消订阅:

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

Why? 为什么? What's the difference between this and unsubscribing from a delegate, eg 这与代表取消订阅有什么区别,例如

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

// ...

d.Barked -= handler;

It all comes down to: when are two delegates considered the same for the purposes of delegate addition / subtraction. 这一切都归结为:为了代表加法/减法的目的,两位代表何时被认为是相同的。 When you unsubscribe, it is essentially using the logic from Delegate.Remove , which considers two delegates equivalent if both the .Target and the .Method match (at least, for the simple case of a delegate with a single target method; multicast is more complicated to describe). 当取消订阅时,它基本上使用从逻辑Delegate.Remove ,它参考等效两位代表如果两个.Target.Method匹配(至少,对于具有单一的目标方法的委托的简单情况;多播是更很难描述)。 So: what is the .Method and .Target on a lambda (assuming we are compiling it to a delegate , and not to an expression )? 所以:是什么.Method.Target在拉姆达(假设我们将它编译成一个代表 ,而不是一个表达式 )?

The compiler actually has a lot of freedom here, but what happens is: 编译器实际上在这里有很多自由,但是会发生什么

  • if the lambda includes a closure over a parameter or variable, the compiler creates a method (the method ) on a compiler-generated class that represents the capture-context (which can also include the this token); 如果lambda包含对参数或变量的闭包,则编译器在编译器生成的类上创建一个方法( 方法 ),该类表示捕获上下文(也可以包含this标记); the target is the reference to this capture-context instance (which will be defined by the capture scope) target是对此捕获上下文实例的引用(将由捕获范围定义)
  • if the lambda doesn't include a closure over a parameter or variable, but does make use of per-instance state via this (implicit or explicit), the compiler creates an instance method (the method ) on the current type; 如果lambda不包含对参数或变量的闭包,但确实通过this (隐式或显式)使用每个实例状态,则编译器在当前类型上创建实例方法( 方法 ); the target is the current instance ( this ) 目标是当前实例( this
  • otherwise the compiler creates a static method (the method ), and the target is null (incidentally, in this scenario it also includes a nifty field to cache a single static delegate instance - so in this scenario , only one delegate is ever created per lambda) 否则编译器会创建一个静态方法( 方法 ),并且目标为null(顺便说一句,在这种情况下,它还包含一个漂亮的字段来缓存单个静态委托实例 - 所以在这种情况下 ,每个lambda只创建一个委托)

What it doesn't do, however, is compare lots of lambdas with similar looking bodies to reduce any. 然而,它没有做的是将许多lambda与具有相似外观的身体进行比较以减少任何。 So what I get when I compile your code is two static methods: 所以我编译代码时得到的是两个静态方法:

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

(the Main here is just because in my test rig those lambdas are inside the Main method - but ultimately the compiler can choose any unpronounceable names it chooses here) (这里的Main是因为在我的测试装备中,那些lambdas在Main方法中 - 但最终编译器可以选择它在这里选择的任何不可发音的名称)

The first method is used by the first lambda; 第一种方法由第一种lambda使用; the second method is used by the second lambda. 第二种方法由第二种lambda使用。 So ultimately, the reason it doesn't work is because the .Method doesn't match. 最终,它不起作用的原因是因为.Method不匹配。

In regular C# terms, it would be like doing: 在常规的C#术语中,它会像:

obj.SomeEvent += MethodOne;
obj.SomeEvent -= MethodTwo;

where MethodOne and MethodTwo have the same code inside them; 其中MethodOneMethodTwo包含相同的代码; it doesn't unsubscribe anything. 它没有取消订阅任何东西。

It might be nice if the compiler spotted this, but it is not required to , and as such it is safer that it doesn't elect to - it could mean that different compilers start producing very different results. 如果编译器发现这一点可能会很好 ,但它不是必需的 ,因此它更安全,它不会选择 - 它可能意味着不同的编译器开始产生非常不同的结果。

As a side note; 作为旁注; it could be very confusing if it did try to de-dup, because you'd also have the issue of capture contexts - it would then be the case that it "worked" in some cases and not others - without being obvious which - probably the worst possible scenario. 如果它确实试图去重复它可能会非常混乱,因为你也有捕获上下文的问题 - 那么它会在某些情况下“工作”而不是其他情况 - 而不是显而易见的 - 可能最糟糕的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM