ZetCode

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 T and an additional null value. For instance, the 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.

Note: the reason why we need nullable value types is because calling databases or online service APIs may return nulls.

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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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 ("").

Program.cs
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

Nullable value types

In this article we have worked with nulls in C#.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all C# tutorials.