C# Nullable Reference Types — Complete Guide
In this tutorial, you'll learn about C# Nullable Reference Types. We cover key concepts, practical examples, and best practices.
You shipped a feature that crashes with NullReferenceException every time a customer hits the edge case you forgot to guard against. Your team spends two hours debugging a null that the compiler could have caught. C# 8.0 introduced nullable reference types so the compiler can track null-state for you.
Wrong
public class OrderService
{
public string GetCustomerEmail(Order order)
{
return order.Customer.Email.ToLower();
}
}
Output: NullReferenceException: Object reference not set to an instance of an object.
The compiler stays silent even though order, order.Customer, or order.Customer.Email could be null.
Right
#nullable enable
public class OrderService
{
public string GetCustomerEmail(Order? order)
{
return order?.Customer?.Email?.ToLower() ?? "no-email@example.com";
}
}
Output: "no-email@example.com" when any part of the chain is null — no crash.
The ? after the type name marks it as nullable. The ?. null-conditional operator short-circuits if the left side is null. The ?? null-coalescing operator provides a default.
Prevention
- Enable nullable in your
.csprojwith<Nullable>enable</Nullable>. - Annotate every parameter, return type, and field as
T?orT. - Treat nullable warnings as errors:
<WarningsAsErrors>Nullable</WarningsAsErrors>. - Use
ArgumentNullException.ThrowIfNull(obj)for non-nullable guard clauses. - Use the null-forgiving operator
!only when you are absolutely certain the value is not null. - Use nullable annotations on public API surfaces for library consumers.
- Enable nullable in new projects by default and migrate existing projects file by file using
#nullable enable.
Common Mistakes with nullable reference
- Using
returnto exit a function early instead of wrapping a pure value in the monad - Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
This pattern is used in Doda Browser's extension pipeline to guarantee that message payloads are never null before dispatch. For more C# best practices, visit DodaTech.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro