The null-forgiving operator ! tells the compiler "trust me, this expression is not null", suppressing any nullable warning that would
otherwise be raised at this position.
When the compiler’s own nullable flow analysis has already narrowed the operand to non-null before the ! is reached (e.g. through a
prior != null check, a switch/is pattern, a loop-exit condition, or a [NotNullWhen]-driven
out parameter), the operator suppresses nothing. It becomes dead code that only adds noise and can mislead readers into thinking the
nullability of the expression is still in doubt at this point.
Why is this an issue?
Every null-forgiving operator is a claim: "I know more about nullability here than the compiler does." When the flow state already proves the
operand non-null, that claim is false, and the operator no longer does anything. Once a codebase accumulates operators like this, readers can no
longer tell which ! occurrences are load-bearing (suppressing a real, justified warning) and which are leftovers from an earlier version
of the code, a refactor, or a copy-paste. This erodes the value of nullable reference types as a tool for reasoning about the code: developers start
distrusting every !, including the ones that matter.
#nullable enable
void M(string? a)
{
if (a != null)
{
_ = a!; // Noncompliant: "a" is already proven non-null here
}
}
How to fix it
Remove the redundant !; the compiler already treats the expression as non-null without it.
Code examples
Noncompliant code example
#nullable enable
void M(string? a)
{
if (a != null)
{
_ = a!; // Noncompliant
}
}
Compliant solution
#nullable enable
void M(string? a)
{
if (a != null)
{
_ = a;
}
}
Resources
Documentation
- Microsoft Learn - ! (null-forgiving) operator
- Microsoft Learn - Nullable reference types
Related rules
- {rule:csharpsquid:S8970} - Null-forgiving operators should not be used when nullable warnings are disabled