Skip to content

Commit 96da513

Browse files
committed
Add ECS0008 use the null conditional operator for event invocations (#48)
This pull request introduces a new analyzer rule, ECS0008, which is designed to enforce the use of the null-conditional operator when invoking event handlers in C#. This practice helps prevent potential `NullReferenceExceptions`, improving the safety and robustness of the code. Key Features: - **Detection of Potential Violations**: The rule identifies patterns where an event handler is invoked without using the null-conditional operator (`?.Invoke`), such as direct invocations or invocations within an `if` statement checking for `null`. - **Automatic Code Fixes**: The code fix provider automatically replaces the identified pattern with the null-conditional operator, ensuring that the event handler is only invoked when it has subscribers. - **Comprehensive Coverage**: The rule handles cases where the event handler is checked directly and cases where it is first assigned to a local variable. Example Violation: ```csharp public class EventSource { private EventHandler<int> Updated; private int counter; public void RaiseUpdates() { counter++; if (Updated != null) Updated(this, counter); } } ``` Fixed code: ```csharp public class EventSource { private EventHandler<int> Updated; private int counter; public void RaiseUpdates() { counter++; Updated?.Invoke(this, counter); } } ```
1 parent d8095ca commit 96da513

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

docs/rules/ECS0008.md

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# ECS0008: Use the Null-Conditional Operator for Event Invocations
2+
3+
This rule is described in detail in [Effective C#: 50 Specific Ways to Improve your C#](https://www.oreilly.com/library/view/effective-c-50/9780134579290/).
4+
5+
## Cause
6+
7+
This rule is triggered when an event handler is invoked without using the null-conditional operator (`?.`), which can potentially lead to a `NullReferenceException` if there are no subscribers to the event.
8+
9+
10+
## Rule description
11+
12+
When invoking events in C#, it is recommended to use the null-conditional operator to ensure that the event is only invoked if it has subscribers. This prevents potential runtime errors and makes the code more robust. This rule checks for patterns where the event handler is invoked directly or after a null check and suggests replacing them with the null-conditional operator.
13+
14+
## How to fix violations
15+
16+
Replace any `if` statement that checks if an event handler is `null` and then invokes the handler with the null-conditional operator.
17+
18+
If the code uses an intermediate variable (e.g., `var handler = Updated;`), remove the variable and replace the `if` statement with the null-conditional operator directly on the event.
19+
20+
## When to suppress warnings
21+
22+
You can suppress warnings from this rule if you're confident that the event will always have subscribers at the time of invocation, or if you have special logic that must be executed before the event is invoked.
23+
24+
## Example of a violation
25+
26+
### Description
27+
28+
Directly invoking the event handler or checking for null before invoking without using the null-conditional operator.
29+
30+
### Code
31+
32+
```csharp
33+
public class EventSource
34+
{
35+
private EventHandler<int> Updated;
36+
private int counter;
37+
38+
public void RaiseUpdates()
39+
{
40+
counter++;
41+
if (Updated != null)
42+
Updated(this, counter);
43+
}
44+
}
45+
46+
public class EventSource
47+
{
48+
private EventHandler<int> Updated;
49+
private int counter;
50+
51+
public void RaiseUpdates()
52+
{
53+
counter++;
54+
var handler = Updated;
55+
if (handler != null)
56+
handler(this, counter);
57+
}
58+
}
59+
```
60+
61+
## Example of how to fix
62+
63+
### Description
64+
65+
Replace the direct invocation or the null check with the null-conditional operator.
66+
67+
### Code
68+
69+
```csharp
70+
public class EventSource
71+
{
72+
private EventHandler<int> Updated;
73+
private int counter;
74+
75+
public void RaiseUpdates()
76+
{
77+
counter++;
78+
Updated?.Invoke(this, counter);
79+
}
80+
}
81+
```

0 commit comments

Comments
 (0)