It's dangerous to code alone! Take this.

Advanced Nullable Reference Types

Modern C# allows you to enlist the compiler’s help in checking for null. The C# Player’s Guide covers the basics of enabling and using this feature. In this article, we will cover two important advanced scenarios for working with nullable reference types.

Fine-Grained Control of Null Checking

When we ask the compiler to help us catch null-related issues, there are two things that it does for us.

The first is that is will allow us to indicate whether null is a legitimate option for a variable, property, or return type. With this turned on, a type ending with a ? indicates null is a possibility, while leaving it off indicates null is not a possibility. For example:

string? name1 = null;
string name2 = "Captain America";

With this feature turned on, the compiler will track the differences between these two types. (It’s a little confusing because with it off, the string type is oblivious to nullability checking, and null is considered a possibility.) The compiler annotates the compiled code with information about whether a thing specifically allows null or specifically forbids null (in contrast to being oblivious to it). This makes it easier for other code to know whether using it can result in null value or not.

The second thing the compiler does is give you warnings when you fail to check for null. If you assign something that could be null to a variable whose type forbids null, you will get a compiler warning. If you attempt to use a variable that could be null without a null check first, you will get a compiler warning.

Most of the time, you will either want both of these features turned on or both turned off. (When you can, you should prefer having them on.) But you are allowed to have neither, either, or both.

When you set a project’s Nullable property to enable, you get both of these features. It will capture the annotations about whether something allows for the possibility of null or expects it to never be null, and you will get compiler warnings when you fail to correctly check.

When you set a project’s Nullable property to disable, you get neither of these features. The compiler is oblivious about whether null is allowed or not, you get no warnings for potential null-related issues, and the annotations (for the most part) don’t get put into the compiled code. In fact, you will get a compiler warning if you attempt to put a ? on the end of a type to indicate nullability with this turned off.

If you set the project’s Nullable property to warnings, you will get compiler warnings when assigning a potential null value to something indicated as not allowing null and when using a something that might be null without checking it for null first. However, the annotations about whether something can be null or not will not be compiled into the binary code.

If you set the project’s Nullable property to annotations, you will not get compiler warnings, but the annotations or metadata will make it into the compiled code.

Enabling Null Checking Per Line

In the rare cases where you cannot enable nullability protections for an entire project but want it for a slice of the code, you can enable within the code itself. This is done by placing the code you want checked inside of #nullable enable and #nullable disable lines:

        public static void Main(string[] args)
            #nullable enable
            string? message1 = null;
            string message2 = null; // Compiler warning
            #nullable disable

With this turned on, the nullability of message1 and message2 will be included in the compiled code, and you will get warnings when failing to correctly handle null, such as on the string message2 = null; line.

These #nullable statements are in a category called preprocessor directives–special instructions to the compiler, rather than part of your C# program itself. Each of these must be placed on their own line with nothing else.

#nullable enable asks the compiler to give you warnings and include nullability annotations in the compiled code for the lines that follow. #nullable disable turns those things off. Code placed between the two will include the extra safeguards.

There is also a #nullable restore directive, which sets the compiler back to whatever the project’s setting is.

If you want to enable, disable, or restore just warnings or just annotations instead of both, the equivalent directives are:

#nullable enable warnings
#nullable disable warnings
#nullable restore warnings

#nullable enable annotations
#nullable disable annotations
#nullable restore annotations

Once these directives have been placed in the code, the new setting stays that way until another one is encountered. That means you can place a #nullable enable at the top of the file without any matching #nullable disable directives and it will apply to the entire file.