This article states You Can’t Unsubscribe from an Event Using a Lambda Expression.
E.g. 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, e.g.
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). So: what is the.Method
and.Target
on a lambda (assuming we are compiling it to a delegate, and not to an expression)?The compiler actually has a lot of freedom here, but what happens is:
this
token); the target is the reference to this capture-context instance (which will be defined by the capture scope)this
(implicit or explicit), the compiler creates an instance method (the method) on the current type; the target is the current instance (this
)What it doesn't do, however, is compare lots of lambdas with similar looking bodies to reduce any. So what I get when I compile your code is two static methods:
(the
Main
here is just because in my test rig those lambdas are inside theMain
method - but ultimately the compiler can choose any unpronounceable names it chooses here)The first method is used by the first lambda; the second method is used by the second lambda. So ultimately, the reason it doesn't work is because the
.Method
doesn't match.In regular C# terms, it would be like doing:
where
MethodOne
andMethodTwo
have the same code inside them; 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.