How Do C#’s Nullable Reference Types Work?

0
241

One of the main features of C# 8.0 is the concept of nullable reference types, which represent a major shift in how programmers can deal with null data. We’ll discuss how they work, and how to use them.

What Are Nullable Reference Types?

There are two main kinds of variables in C#. Value types have fixed sizes, like int, float, and bool. They’re passed between functions by value, and usually stored on the stack—very fast memory that gets cleaned off when going out of scope. They can never be null, but by using the Nullable<T> struct, value types can be made to support null data if you want that behaviour.

RELATED: How Does Memory Management Work in C#?

The other kind of type is reference types, which are larger objects without a fixed size, like strings and lists. They are almost always stored on the heap, with the variable on the stack being a reference to the memory location.

Problem is, reference types can go null. The variable that stores the location can be set to a null value, which is pretty common especially when dealing with data that isn’t guaranteed to be there, like optional questions in a web form. This is why .NET needs a garbage collector, to clean up objects that no longer have any active references.

If you’re a .NET programmer, you’re definitely used to null-checking, which is where you manually check if something has gone null before using it. This works well, and it’s a very cheap operation to do, but in many cases it’s unnecessary. For a reference type to be null, it has to either not have been initialized with a proper value, or have been manually assigned to the value of null, e.g.,

variable = null;

Nullable reference types are a new addition that essentially enforce a difference between reference variables that can go null, and reference variables that can’t. It’s a breaking feature that will likely leave your codebase with a lot of warnings, so it’s something you have to manually turn on. Once it’s on, the compiler start to tell the difference between:

  • string?, which can be null, and retains the “default” behavior from earlier versions, and
  • string, which cannot be null. It can never be null, because it must be given a default value, and can never be set to null.

With the feature enabled, reference types will work in largely the same way as value types—never going null unless you tell the compiler it can with the Type? syntax. Technically, “nullable reference types” are what C# has had forever, and the new feature is the non-nullable reference types that replace the old ones.

This simple feature allows you to inform the compiler about your intentions for the variable. If you try to assign a nullable string? value to a non-nullable string variable, you’ll get a warning that you’re not handling null properly.

To fix this warning, you’ll need to set the non-nullable value only after checking if it’s not null. The compiler is smart, and is aware of when and where the value may be null. If you wrap it in a if (value != null) block, it won’t give you an warning, and will remind you that it’s not null when using it.

Unlike nullable value types, nullable reference types are implicitly converted to their non-nullable equivalents, albeit with a warning.

You can use nullable reference types anywhere you can use regular types, whether that’s as a local variable, fields, or properties for classes and structs, and input parameters for functions. If you try to convert them to non-null without checking, you’ll get an error.

How to Turn Nullable Context On

From the Solution Explorer, right-click your project and select “Edit Project File.” You might need to unload it first to see this option.

<Nullable>enable</Nullable>

If you are using the legacy project format, you might need to manually override this with a directive at the top of each file:

#nullable enable

If you don’t want to enable warnings, you can use the “Annotation” context, which will only show annotations when you hover over them.