rspec/rules/S3244/csharp/rule.adoc
2023-07-03 15:45:11 +02:00

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[]