<PutYourCoverAltHere>

Goodbye, NullReferenceException: Nullable Operators and References

In the previous post of this series, we covered two ideas to avoid the NullReferenceException: we should check for null before accessing the members of an object and check the input parameters of our methods.

Let’s see some new C# operators to simplify null checking and a new feature to better signal possible null references.

1. C# Nullable Operators

C# has introduced new operators to simplify our null checks: ?., ??, and ??=. These operators don’t prevent us from having null in the first place, but they help us to easily write our null checks.

Without Nullable Operators

Let’s start with an example and refactor it to use these new operators.

string path = null;

if (HttpContext.Current == null 
    || HttpContext.Current.Request == null 
    || HttpContext.Current.Request.ApplicationPath  == null)
{
    path = "/some-default-path-here";
}
else
{
    path = HttpContext.Current.Request.ApplicationPath;
}

If you have worked with the old ASP.NET framework, you might have done something like that. If not, don’t worry. We’re only accessing a property down in a property chain, but any of those properties could be null.

Notice that to defend against null, we checked if every property was null to “fail fast” and use a default value.

With Nullable Operators

Now, let’s use the new nullable operators instead,

var path = HttpContext.Current?.Request?.ApplicationPath
//                           ^^^      ^^^
              ?? "/some-default-path-here";
//           ^^^^

More compact, right?

With the null-conditional operator (?.), we access the property or method of an object only if the object isn’t null. Otherwise, the entire expression evaluates to null. For our example, we retrieve ApplicationPath only if Request isn’t null and Current isn’t null, and HttpContext isn’t null.

Then, with the null-coalescing operator (??), we evaluate an alternative expression if the one on the left of the ?? operator isn’t null. For our example, if any of the properties in the chain to retrieve ApplicationPath is null, the whole expression is null, and the path variable gets assigned to the default string.

With the null-coalescing assignment operator (??=), we assign a new value to a variable only if it’s null. We could also write our example like this,

var path = HttpContext.Current?.Request?.ApplicationPath;
path ??= "/some-default-path-here";
//  ^^^^
//
// The same as
//if (path == null)
//{
//    path = "/some-default-path-here"; 
//}

Notice how we refactored our original example to only two lines of code with these three new operators. Again we could still have null values. These nullable operators make our lives easier by simplifying our null checks.

Electronic devices in a workbench
What if we could tell when something is null? Photo by Nicolas Thomas on Unsplash

2. C# Nullable References

To solve the NullReferenceException, we should check for null. We got that! But the thing is knowing when we should do it or not. That’s precisely what C# 8.0 solves with Nullable References.

With C# 8.0, all reference variables are non-nullable by default. Accessing the member of nullable references results in compiler warnings or errors.

This is a breaking change. Therefore we need to turn on this feature at the project level in our csproj files. Like these,

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <!--             ^^^^^^ -->
    <!-- We could use netcoreapp3.1|net5.0|net6.0|.net7.0 too -->
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <!--      ^^^^^^ -->
    <!-- We could use enable|disable|warning too -->
    <WarningsAsErrors>nullable</WarningsAsErrors>
    <!--              ^^^^^^^^ -->
    <!-- We can take the extreme route -->

  </PropertyGroup>

</Project>

To use Nullable References, we need to target .NET Core 3 and upward. And inside the Nullable node in our csproj files, we could use: enable, disable, or warning. Even, we can take the extreme route and consider all nullable warnings as compilation errors.

With Nullable References On

Let’s see what our motivating example looks like with Nullable References turned on,

string path = null;
//            ^^^^
// CS8600: Converting null literal or possible null value to non-nullable type

if (HttpContext.Current == null
    || HttpContext.Current.Request == null
    || HttpContext.Current.Request.ApplicationPath == null)
{
    path = "/some-default-path-here";
}
else
{
    path = HttpContext.Current.Request.ApplicationPath;
}

// This isn't the real HttpContext class...
// We're writing some dummy declarations to prove a point
public class HttpContext
{
    public static HttpContext Current;
    //                        ^^^^^^^^
    // CS8618: Non-nullable field 'Current' must contain
    // a non-nullable value when exiting constructor

    public HttpContext()
    //     ^^^^^^^^^^^
    // CS8618: Non-nullable field 'Current' must contain
    // a non-nullable value when exiting constructor
    {
    }

    public Request Request { get; set; }
}

public record Request(string ApplicationPath);

Notice we have a warning when initializing path to null. And another one in the declaration of our HttpContext class if we don’t initialize any non-nullable fields. That’s not the real HttpContext by the way but bear with me. Also, we don’t need to check for null when retrieving the ApplicationPath since all our references aren’t nullable by definition.

To declare a variable that can be null, we need to add to its type declaration a ?. In the same way, we have always declared nullable primitive types like int?.

Without Null Checks

Let’s change our example to have nullable references and no null checks,

// Notice the ? symbol here
//   vvv
string? path = HttpContext.Current.Request.ApplicationPath;
//             ^^^^^^^^^^^
// CS8602: Deference of a possibly null value

// This isn't the real HttpContext class...
// We're writing some dummy declarations to prove a point
public class HttpContext
{
    public static HttpContext? Current;
    //                      ^^^
    // Notice the ? symbol here

    public HttpContext()
    //     ^^^
    // No more warnings here
    {
    }

    public Request Request { get; set; }
}

public record Request(string? ApplicationPath);

Notice this time, when declaring the variable path, we have a warning because we’re accessing the Current property, which might be null. Also, notice we changed, inside the HttpContext class, the Current property to have the HttpContext? type (with a ? at the end of the type).

Now with Nullable References, we have a way of telling when we should check for null by looking at the signature of our methods.

Voilà! Those are the new C# operators to simplify our null checks. We said “new” operators, but we have had them since C# 6.0. And that’s how we can tell if our references can be null or not using Nullable References.

We have these nullable operators available even if we’re using the old .NET Framework. But, to use Nullable References, we should upgrade at least to .NET Core 3.0.

In the next post, we will cover the Option type as an alternative to null and how to avoid the NullReferenceException when working with LINQ.

If you want to read more C# content, check my C# Definitive Guide and my top of recent C# features. These three operators and the nullable references are some of them.

Happy coding!