C# nullable types
last modified July 5, 2023
C# nullable types tutorial shows how to work with nullable values in C#.
The null is a unique data type which represents a value that is missing
or does not exist. NullReferenceException
is thrown when there is
an attempt to dereference a null
object reference. A lot of effort
has been put into preventing these exceptions.
The two fundamental data types in C# are value types and reference types. Value
types cannot be assigned a null
literal. The default value for
reference types is null
reference.
Over the years, C# included many features to handle null values.
Null safety is a set of features specific to nullable types that help
reduce the number of possible NullReferenceException
occurrences.
C# 2.0 introduced nullable value types. Since then it is possible to
assign null values to value types using the T?
syntax.
C# 8.0 introduced nullable reference types. With nullable reference
types we can express our intent that a reference type might be null or is always
non-null. The same T?
syntax expresses that a reference type is
intended to be nullable.
Nullable context
Nullable contexts enable fine-grained control for how the compiler interprets reference type variables. Since .NET 6 null-state analysis and variable annotations are enabled by default in new projects.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
On a project level, the nullable context is set with the Nullable
tag.
#nullable enable
It is possible to scope nullable context to a C# file using a compiler directive.
C# nullable value type
A nullable value type T?
represents all values of its underlying
value type bool?
variable can be assigned one of true
,
false
, or null
.
T?
is a shorthand for System.Nullable<T>
structure. The HasValue
returns a value indicating whether the
current System.Nullable<T>
has a valid value of its
underlying type. The Value
unpacks the value from the nullable
value type.
A nullable value type has the same assignment rules as a value type. Local variables in methods must be assigned before using them. Fields in classes will have a null value by default.
C# Nullable example
In the next example, we work with nullable value types.
Nullable<int> x = 0; Console.WriteLine(x); int? y = 0; Console.WriteLine(y); Console.WriteLine("-------------------------"); x = null; if (x == null) { Console.WriteLine("null value"); } y = null; if (y == null) { Console.WriteLine("null value"); }
In the example, we create two nullable integer variables. First they are
initalized to zero, then they are assigned null
.
$ dotnet run 0 0 ------------------------- null value null value
C# check for null
We can use the !=
and is not
operators to check for
null values.
string?[] words = { "sky", "black", "rock", null, "rain", "cup" }; foreach (var word in words) { if (word != null) { Console.WriteLine($"{word} has {word.Length} letters"); } } Console.WriteLine("-------------------------"); foreach (var word in words) { if (word is not null) { Console.WriteLine($"{word.ToUpper()}"); } }
We have a list of words. To prevent NullReferenceExceptions
, we
ensure that each value is not null before calling the element's attribute or
method.
With HasValue
attribute of Nullable
, we check if the
current nullable element has a valid value of its underlying type.
int?[] vals = { 1, 2, 3, null, 4, 5 }; int sum = 0; foreach (var e in vals) { if (e.HasValue) { sum += e.Value; } } Console.WriteLine($"The sum is: {sum}");
In the example, wee calculate the sum of integer values.
if (e.HasValue) { sum += e.Value; }
We check if the element has some value with HasValue
and unpack
the value via Value
.
C# null-conditional operator
A null-conditional operator applies a member access, ?.
,
or element access, ?[]
, operation to its operand only if that
operand evaluates to non-null. If the operand evaluates to null
,
the result of applying the operator is null.
var users = new List<User> { new User("John Doe", "gardener"), new User(null, null), new User("Lucia Newton", "teacher") }; users.ForEach(user => Console.WriteLine(user.Name?.ToUpper())); record User(string? Name, string? Occupation);
In the example, we have a User record with two members: Name
and
Occupation
. We access the name member of the objects with the help
of the ?.
operator.
users.ForEach(user => Console.WriteLine(user.Name?.ToUpper()));
We use the ?.
to access the Name
member and call the
ToUpper
method. The ?.
prevents the
System.NullReferenceException
by not calling the
ToUpper
on the null
value.
The ?[]
operator allows to place null
values into a
collection.
int?[] vals = { 1, 2, 3, null, 4, 5 }; int i = 0; while (i < vals.Length) { Console.WriteLine(vals[i]?.GetType()); i++; }
We have a null
value in an array. We prevent the
System.NullReferenceException
by applying the ?.
operator on the array elements.
C# ArgumentNullException
The ArgumentNullException
is thrown when a null reference is passed
to a method that does not accept it as a valid argument.
var words = new List<string?> { "falcon", null, "water", "war", "fly", "wood", "forest", "cloud", "wrath" }; foreach (var word in words) { int n = 0; try { n = CountLtr(word, 'w'); Console.WriteLine($"{word}: {n}"); } catch (ArgumentNullException e) { Console.WriteLine($"{e.Message}"); } } int CountLtr(string? word, char c) { // if (word is null) // { // throw new ArgumentNullException(nameof(word)); // } ArgumentNullException.ThrowIfNull(word); return word.Count(e => e.Equals(c)); }
In the example, we throw and catch ArgumentNullException
.
$ dotnet run falcon: 0 Value cannot be null. (Parameter 'word') water: 1 war: 1 fly: 0 wood: 1 forest: 0 cloud: 0 wrath: 1
C# null-forgiving operator
In the enabled nullable context, the null-forgiving operator (!) supresses the compiler warnings. The operator has no effect at run time. It only affects the compiler's static flow analysis.
The following example uses the Playwright library; check the Playwright tutorial for more information.
using Microsoft.Playwright; using System.Text; using var pw = await Playwright.CreateAsync(); await using var browser = await pw.Chromium.LaunchAsync(); var page = await browser.NewPageAsync(); var ehds = new Dictionary<string, string>{ {"User-Agent", "C# program" } }; await page.SetExtraHTTPHeadersAsync(ehds); var resp = await page.GotoAsync("http://webcode.me/ua.php"); var body = await resp!.BodyAsync(); Console.WriteLine(Encoding.UTF8.GetString(body));
In the example, we request a resource which returns a response containing the user aget used.
var resp = await page.GotoAsync("http://webcode.me/ua.php"); var body = await resp!.BodyAsync();
The response may be null when the website is down our we have connection issues. But we explicitly showed the intent not to worry about this.
C# null-coalescing operator
The null-coalescing operator ??
returns the value of its left-hand
operand if it isn't null; otherwise, it evaluates the right-hand operand and
returns its result.
var words = new List<string?> { "work", "falcon", null, "cloud", "forest" }; foreach (var word in words) { var e = word ?? "null value"; Console.WriteLine(e); }
We use the ??
operator to print "null value" instead of the default
empty string.
C# null-coalescing assignment operator
The null-coalescing assignment operator ??=
assigns the value of
its right-hand operand to its left-hand operand only if the left-hand operand
evaluates to null
.
List<int>? vals = null; vals ??= new List<int> { 1, 2, 3, 4, 5 }; // if (vals == null) // { // vals = new List<int> { 1, 2, 3, 4, 5 }; // } vals.Add(6); vals.Add(7); vals.Add(8); Console.WriteLine(string.Join(", ", vals)); vals ??= new List<int> { 1, 2, 3, 4, 5 }; Console.WriteLine(string.Join(", ", vals));
We use the null-coalescing assignment operator on a list of integer values. The equivalent code without using the operator is commented out.
List<int>? vals = null;
First, the list is assigned to null
.
vals ??= new List<int> { 1, 2, 3, 4, 5 };
We use the ??=
to assign a new list object to the variable. Since
it is null
, the list is assigned.
vals.Add(6); vals.Add(7); vals.Add(8); Console.WriteLine(string.Join(", ", vals));
We add three values to the list and print its contents.
vals ??= new List<int> { 1, 2, 3, 4, 5 };
We try to assign a new list object to the variable. Since the variable
is not null
anymore, the list is not assigned.
$ dotnet run 1, 2, 3, 4, 5, 6, 7, 8 1, 2, 3, 4, 5, 6, 7, 8
C# string.IsNullOrWhiteSpace
The IsNullOrWhiteSpace
method indicates whether a specified string
is null, empty, or consists only of white-space characters.
var words = new List<string?> { "\t", "falcon", " ", null, "\n", "cloud", "" }; for (int i = 0; i < words.Count; i++) { var e = words[i]; if (string.IsNullOrWhiteSpace(e)) { Console.WriteLine($"{i}: null or white space"); } else { Console.WriteLine($"{i}: {e}"); } }
In the example, we use the IsNullOrWhiteSpace
method on a list of
nullable string elements.
$ dotnet run 0: null or white space 1: falcon 2: null or white space 3: null or white space 4: null or white space 5: cloud 6: null or white space
C# string.IsNullOrEmpty
The IsNullOrEmpty
method indicates whether the specified string is
null or an empty string ("").
var words = new List<string?> { "\t", "falcon", " ", null, "\n", "cloud", "" }; for (int i = 0; i < words.Count; i++) { var e = words[i]; if (string.IsNullOrEmpty(e)) { Console.WriteLine($"{i}: null or empty"); } else { Console.WriteLine($"{i}: {e}"); } }
In the example, we use the IsNullOrEmpty
method on a list of
nullable string elements.
$ dotnet run 0: 1: falcon 2: 3: null or empty 4: 5: cloud 6: null or empty
Source
In this article we have worked with nulls in C#.
Author
List all C# tutorials.