Using C# Nullable Reference Types to Catch Null Bugs Early
Enable C# nullable reference types to turn potential NullReferenceExceptions into compile‐time warnings, then fix them with null‐conditional operators or explicit checks.
22 Feb 2026, 10:33 UTC

The problem: silent null dereferences
In many .NET applications a NullReferenceException surfaces only at runtime, often after a change has been merged and deployed. The exception tells you that a reference was null when you tried to use it, but it gives little context about where the null originated. This makes debugging costly and can lead to unstable releases.
Enabling nullable reference types
C# 8.0 introduced nullable reference types, a compile‐time analysis that lets you annotate whether a reference type may be null. By adding <Nullable>enable</Nullable> to your project file (or using #nullable enable at the top of a file) you tell the compiler to treat every reference type as non‐null unless you explicitly mark it with ?. The compiler then warns when you dereference a variable that could be null without a check.
Seeing the warning in action
Create a fresh console project to experiment:
- Open a terminal with write access to a folder.
- Run
dotnet new console -n NullableDemoto generate a project. - Change directory:
cd NullableDemo. - Edit
NullableDemo.csprojand add the nullable element inside<PropertyGroup>:<Nullable>enable</Nullable> - Replace the generated
Program.cswith the following code:using System; string? GetUserName() => null; void PrintLength() { var name = GetUserName(); Console.WriteLine(name.Length); // CS8602 warning } PrintLength(); - Build the project:
dotnet build.
The compiler emits a warning similar to:
CS8602: Possible dereference of a null reference.
This occurs because GetUserName returns a string? (explicitly nullable) but the code treats the result as a non‐null string when calling Length.
Fixing the warning
You have two idiomatic ways to satisfy the compiler:
- Use the null‐conditional operator:
Console.WriteLine(name?.Length ?? 0);
- Or perform an explicit null check:
if (name != null)
{
Console.WriteLine(name.Length);
}
else
{
Console.WriteLine(0);
}
After applying either fix, run dotnet build again. The warning disappears, indicating that the compiler can now prove the dereference is safe.
Trade‐offs and limitations
- Migration effort: Enabling the feature in a large existing codebase can produce hundreds of warnings. Teams often adopt a gradual approach—enable nullable for new files first, then fix existing files incrementally.
- Compile‐time only: The analysis does not stop null values that arrive via reflection, serialization, or interop with untyped code. Runtime guards (e.g.,
ArgumentNullExceptionchecks) remain necessary for public APIs. - False sense of security: Treating warnings as errors (
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>) helps enforce discipline, but developers must still review code that uses!(null‐forgiving operator) or#nullable disablesections.
Actionable closing
- Start a new project or a feature branch with
<Nullable>enable</Nullable>in the csproj. - Build and observe any CS8602‐family warnings; treat them as actionable items.
- Fix each warning using null‐conditional operators, explicit checks, or, when appropriate, the null‐forgiving operator with a clear comment.
- Consider configuring
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>once the baseline is clean to prevent regressions. - Document the decision in your team’s contribution guide so newcomers know why nullable reference types are enabled.
By making nullability explicit in the type system, you shift many null‐related defects from runtime to compile time, reducing surprise exceptions and improving code readability.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.