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) => Consol
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:
[CompilerGenerated]
private static void b__0(object s, string e)
{
Console.WriteLine("Bark: {0}", e);
}
[CompilerGenerated]
private static void 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)
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:
obj.SomeEvent += MethodOne;
obj.SomeEvent -= MethodTwo;
where MethodOne
and MethodTwo
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.