- C# - Home
- C# - Overview
- C# - Environment
- C# - Program Structure
- C# - Basic Syntax
- C# - Data Types
- C# - Type Conversion
- C# - Variables
- C# - Constants
- C# - Operators
- C# - Arithmetic Operators
- C# - Assignment Operators
- C# - Relational Operators
- C# - Logical Operators
- C# - Bitwise Operators
- C# - Miscellaneous Operators
- C# - Operators Precedence
- C# Conditional Statements
- C# - Decision Making
- C# - If
- C# - If Else
- C# - Nested If
- C# - Switch
- C# - Nested Switch
- C# - Switch Expressions
- C# Control Statements
- C# - Loops
- C# - For Loop
- C# - While Loop
- C# - Do While Loop
- C# - Nested Loops
- C# - Break
- C# - Continue
- C# - Foreach Loop
- C# - Goto Statement
- C# OOP & Data Handling
- C# - Encapsulation
- C# - Methods
- C# - Nullables
- C# - Arrays
- C# - Strings
- C# - Structure
- C# - Enums
- C# - Classes
- C# - Inheritance
- C# - Polymorphism
- C# - Operator Overloading
- C# - Interfaces
- C# - Namespaces
- C# - Preprocessor Directives
- C# - Regular Expressions
- C# - Exception Handling
- C# - File I/O
- C# Advanced Tutorial
- C# - Attributes
- C# - Reflection
- C# - Properties
- C# - Indexers
- C# - Delegates
- C# - Events
- C# - Collections
- C# - Generics
- C# - LINQ
- C# - IEnumerable vs IEnumerator
- C# - Anonymous Methods
- C# - Unsafe Codes
- C# - Tasks and Parallel Programming
- C# - Multithreading
- C# - Extension Methods
- C# - Async and Await
- C# Modern Features
- C# - Tuples
- C# - Pattern Matching Enhancements
- C# - Nullable Reference Types
- C# - What's New in C# 11 / 12 / 13
- C# Practical & Advanced Usage
- C# - JSON & XML Handling
Nullable Reference Types in C#
Nullable reference types in C# are a set of features that were introduced in C# 8.0 and help to minimise the chances of the runtime throwing a System.NullReferenceException.
Nullable reference types have three main features that help avoid these exceptions, including marking a reference type as nullable −
-
Improved flow analysis − C# can now check if a variable might be
nullbefore we use it. -
API annotations − Special attributes help C# understand if a value from an API can be
nullor not. -
Variable annotations − Developers can mark variables to show whether they can be
null.
What Are Nullable Reference Types?
A nullable reference type in C# does not change how our code runs; instead, it enhances compile-time checking. The nullable reference type helps the compiler track which variables are nullable (can hold null) and which are non-nullable (should never hold null).
Nullable reference types help developers write safer and more reliable code by letting the compiler warn us when a variable might be null.
Now let's learn how the three features work to produce warnings when your code might dereference a null value. Dereferencing a variable means to access one of its members using the .(dot) operator, as shown in the following example −
string message = "Hello, World!"; // dereferencing "message" int length = message.Length;
When we dereference a variable whose value is null, the runtime throws a System.NullReferenceException.
Similarly, warnings can be produced when [] notation is used to access a member of an object when the object is null −
using System;
public class Collection<T> {
private T[] array = new T[100];
public T this[int index] {
get => array[index];
set => array[index] = value;
}
}
public class myProgram{
public static void Main(){
Collection<int> c = default;
// CS8602: Possible dereference of null
c[10] = 1;
}
}
Declaring Nullable and Non-Nullable Types
We can declare once the nullable reference types are enabled −
- Non-nullable reference types cannot hold
nullvalues. - Nullable reference types can hold
null, and are marked with a?symbol.
string name = "Aman"; // Non-nullable string? nickname = null; // Nullable
If we try to assign null to name, the compiler will show a warning −
string name = null; // Warning: Possible null assignment
Preventing Null Reference Warnings (Dereferencing)
Warnings are produced when we dereference a variable whose value is null, which causes the runtime to throw a System.NullReferenceException. Dereferencing means accessing one of its members using the '.' (dot) operator or '[]' notation.
string message = "Hello, World!"; // Dereferencing "message" (safe, as it's non-nullable) int length = message.Length;
C# compiler specifically warns when we try to dereference a nullable reference type before checking its value:
using System;
public class myProgram {
public static void Main() {
// The result might be null
string? message = GetMessage();
// Warning (CS8602): Possible dereference of a null reference.
// int length = message.Length;
// Correct Way: Use Improved Flow Analysis
if (message != null){
// No warning here
int safeLength = message.Length;
}
}
static string? GetMessage() => new Random().Next(2) == 1 ? "Hello" :null;
}
The Null-Forgiving Operator (!)
The null-forgiving operator (or "dammit operator"), denoted by an exclamation mark (!), is a tool to suppress a compiler warning. It is a runtime assertion; we are telling the compiler, "Trust me, we guarantee this value is not null here."
The '!' operator does not change the runtime value. If the variable is actually null, a NullReferenceException will still occur when dereferenced.
string? nullableMessage = GetMessage(); // Using '!' to force the compiler to treat it as non-null int length = nullableMessage!.Length; // Compiler warning suppressed
Enabling Nullable Reference Types
The feature is opt-in and requires explicit enabling in our project. We must be using C# 8.0 or later.
Project-Level Enabling (Recommended)
Add the following tag within a '<PropertyGroup>' element in your '.csproj' file. This is the most consistent approach.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
File-Level Directives
For large codebases or gradual migration, we can control the setting on a file-by-file basis −
#nullable enable // Turns on NRT warnings for the rest of the file // ... your code here ... #nullable disable // Turns off NRT warnings for subsequent code
Donât confuse Nullable Reference Types (like string? in C# 8.0) with Nullable Value Types (like int? in C# 2.0). Although both use the ? symbol, they work differently. Nullable Value Types allow value types (such as int, bool, etc.) to hold null, while Nullable Reference Types help you avoid null reference errors by tracking where null might appear in your code.
| Feature | Nullable Reference Type ('string?') | Nullable Value Type ('int?') |
|---|---|---|
| Introduced | C# 8.0 | C# 2.0 |
| Mechanism | Compile-time annotation/static analysis. | Uses the System.Nullable<T> struct at runtime. |
| Purpose | Prevent runtime NullReferenceException. | Allows a value type (which cannot normally be null) to represent missing data. |
Conclusion
Nullable Reference Types make our code safer and more reliable. They help us find potential null issues during compilation instead of at runtime. We can reduce the chances of getting a System.NullReferenceException by marking the reference types as nullable or non-nullable and by using compiler warnings effectively.