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;
Note that the subscriber class needs a reference to the publisher class in order to subscribe to its events. You can also use a lambda expression to specify an event handler: If you don't have to unsubscribe from an event later, you can use the addition assignment operator ( +=) to attach an anonymous function as an event handler.
The lambda function is triggered, it will inspect its event and perform a logic on the event before returning and terminating. Now that we've covered event, let's move onto context. context is a Python objects that implements methods and has attributes.
When you hit the Test button on the console, you invoked the lambda and passed it the test event -- the lambda then took that event and printed it out. As we dive deeper into AWS Lambda, we'll find more and more possible events that can trigger lambdas, such as:
Each event will have a different format and carry different payloads, but the pattern is always the same. The lambda function is triggered, it will inspect its event and perform a logic on the event before returning and terminating. Now that we've covered event, let's move onto context.
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 <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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With