64 lines
3.1 KiB
Plaintext
64 lines
3.1 KiB
Plaintext
== Why is this an issue?
|
|
|
|
When working with https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions[anonymous functions], it is important to keep in mind that each time you create one, it is a completely new instance.
|
|
|
|
In this example, even though the same lambda expression is used, the expressions are stored separately in the memory and are therefore not equal or the same.
|
|
|
|
[source,csharp]
|
|
----
|
|
Func<int, int> lambda1 = x => x + 1;
|
|
Func<int, int> lambda2 = x => x + 1;
|
|
|
|
var result = lambda1 == lambda2; // result is false here
|
|
----
|
|
|
|
This is even more true when working with https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/[events] since they are https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/how-to-combine-delegates-multicast-delegates[multicast delegates] that offer ways of https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events[subscribing and unsubscribing] to them. If an anonymous function is used to subscribe to an event, it is impossible to unsubscribe from it. This happens because to remove the entry from the subscription list, a reference to the original method is needed, but if the anonymous function has not been stored before subscribing, there is no way to find a reference to it.
|
|
|
|
Instead, store the callback to a variable or a named method and use the variable or method to subscribe and unsubscribe.
|
|
|
|
== How to fix it
|
|
|
|
Store the callback to a variable or a named method and use the variable or method to subscribe and unsubscribe.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,csharp,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
event EventHandler myEvent;
|
|
|
|
void DoWork()
|
|
{
|
|
myEvent += (s, e) => Console.WriteLine($"Event raised with sender {s} and arguments {e}!");
|
|
// ...
|
|
myEvent -= (s, e) => Console.WriteLine($"Event raised with sender {s} and arguments {e}!"); // Noncompliant: this callback was never subscribed
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,csharp,diff-id=1,diff-type=compliant]
|
|
----
|
|
event EventHandler myEvent;
|
|
void LogEvent(object s, EventArgs e) => Console.WriteLine($"Event raised with sender {s} and arguments {e}!");
|
|
|
|
void DoWork()
|
|
{
|
|
myEvent += LogEvent;
|
|
// ...
|
|
myEvent -= LogEvent; // Compliant: LogEvent points to the same callback used for subscribing
|
|
}
|
|
----
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/[Events (C# Programming Guide)]
|
|
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions[Lambda expressions and anonymous functions]
|
|
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events[How to subscribe to and unsubscribe from events (C# Programming Guide)]
|
|
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/how-to-combine-delegates-multicast-delegates[How to combine delegates (Multicast Delegates) (C# Programming Guide)]
|
|
|
|
include::../rspecator.adoc[]
|