[C#][8.0] Nullable Reference Types

Table of Contents

Introduction

As you know, C# 8.0 released at September 2019. It was available .NET Core 3.0, .NET Standard 2.1 frameworks. So that, we could get it from Visual Studio 2019 maybe from version 16.3.

The Nullable Reference Types is a new feature which require library and runtime features supported from .NET Core 3.x. It makes use of several attributes to provide better warnings. Those attributes were added in .NET Core 3.0 [Click here]. Other target frameworks haven’t been annotated with any of these attributes. That means the nullable warnings may not accurately reflect potential issues.

Before we dive in to concepts of the Nullable Reference Types feature, we should know a little bit about who is the person invented “null reference” feature ? and What is his billion-dollar mistake ?

Tony Hoare’s The billion-dollar mistake

Sir Charles Antony Richard Hoare (born 11 January 1934) is a British computer scientist. He developed the sorting algorithm quicksort in 1959–1960. Many years later, at a software conference in 2009, Sir Tony actually apologized and called it his “billion-dollar mistake”.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.” – Tony Hoare apologized

Because of that mistake, The C# Language need to an approach to make more clear about where should be use null value as default for reference type ? where should be use non-null value as default for reference type ? That is the reason we have the Nullable Reference Types feature in C# 8.0.

Nullable Reference Types

The first one, you should be aware of that what is References Type? The Reference Type is a Class Type, a Interface Type, an Array Type, or a Delegate Type.

In earlier C# 7.3, the value of Reference Type is a reference to an instance of the type. The special value “null” is compatible with all reference types and indicates the absence of an instance. So, developers can understand that when a variable is declared with Reference Type. It is meant this variable accept non-nullable (instance of Reference Type) and nullable value (null) without explicitly declaration.

Reference Type in C# 7.3 earlier

In C# 8.0, The Reference Type is split into 2 explicit types. The one is Non-Nullable Reference Types and the remaining is Nullable Reference Types. Inside a nullable annotation context, any variables of a reference type is considered to be a Non-Nullable Reference Type. If you want to indicate that a variable may be null, you must append the type name with the “?” to declare the variable as a Nullable Reference Type.

For Non-Nullable Reference Types, the compiler use flow analysis to ensure that local variables are initialized to a non-null value when declared. Its fields must be initialized during construction. The compiler generates a warning if the variable isn’t set by a call to any of the available constructor or by an initializer. Furthermore, non-nullable reference types can’t be assigned a value that could be null.

Nullable Reference Types aren’t checked to ensure they aren’t assigned or initialized to null. However, the compiler uses flow analysis to ensure that any variable of a nullable reference type is checked against null value before it’s accessed or assigned to a non-nullable reference type.

Both of Reference Types in C# 8.0

Benefits of Nullable Reference Types

Traditionally, Developers considered that reference type is meant to accept both null and non-null value. There wasn’t any explicit handling required and unfortunately this consideration is one of the primary root causes of famous “Null Reference Exception”.

The primary reason for “Null Reference Exception” is that reference doesn’t have any valid memory address and hence while de-referencing (reading value from memory address), it doesn’t know from where to read the value and then failed.

Splitting Reference Type into Non-Nullable and Nullable helps to clear the intent during the design phase.

Nullable Context

The Nullable Contexts control how the compiler interprets reference type variables. There are 2 contexts in Nullable Contexts as below.

  • Nullable Annotation Context could be enabled or disabled. This context tells to compiler that should run new rules for Nullable Reference Types or run as earlier C# 7.3.
  • Nullable Warnings Context could be enabled or disabled. The context specifies the warnings generated by the compiler using its flow analysis.

The Nullable Contexts can be set for a project using 2 ways as follow.

  • Set up on “properties” of the project.
Setting Nullable Context in a project on VS2019
  • Set up manual on “.csproj” file of the project.
Setting Nullable Context in a “.csproj” file manually

The “Nullable” element have 4 options to configure. Each option will set State of Nullable Annotation Context and State of Nullable Warning Context as below.

Value of the “Nullable” elementState of Nullable Annotation ContextState of Nullable Warning Context
enableenabled enabled
warningsdisabled enabled
annotationsenabled disabled
disabledisabled disabled

Also you can use directives to set/override the same contexts anywhere in your project.

Nullable directives
  • #nullable enable: Set the nullable annotation context and nullable warning context to enabled.
  • #nullable disabled: Set the nullable annotation context and nullable warning context to disabled.
  • #nullable restore: Restore the nullable annotation context and nullable warning context to the project settings.
  • #nullable disable warnings: Set the nullable warning context to disabled.
  • #nullable enable warnings: Set the nullable warning context to enabled.
  • #nullable restore warnings: Restore the nullable warning context to the project settings.
  • #nullable disable annotations: Set the nullable annotation context to disabled.
  • #nullable enable annotations: Set the nullable annotation context to enabled.
  • #nullable restore annotation: Restore the annotation warning context to the project settings.

Nullable Annotation Context

When Nullable Annotation Context is disabled, the compiler uses the following rules.

  • Developer can’t declare Nullable Reference Type.
  • All Reference Variables may be assigned Null value.
  • No warnings are generated when a variable of a reference type is dereferenced (reading value from memory address).
  • The null-forgiving operator may not be used in a disabled context.

All above rules is same behavior as previous version of C# (earlier C# 7.3). When Nullable Annotation Context is Enabled, the compiler uses the following rules.

  • Any variables of a reference type is a non-nullable reference (declare reference type without the “?” appended).
  • Any non-nullable reference may be dereferenced safely.
  • Any nullable reference type (declare reference type with the “?” is appended) may be null. Static analysis determines if the value is known to be non-null when it’s dereferenced. If not, the compiler warns you.
  • Developer can you the null-forgiving operator to declare that a nullable reference isn’t null.

Nullable Warning Context

The compiler uses static flow analysis to determine the null state of any reference. The null state is either not null or maybe null when the nullable warning context isn’t disabled. If developer dereference a reference when the compiler has determined it’s maybe null, the compiler warns you. The state of a reference is maybe null If the compiler determines one of 2 conditions as following.

  • The variable (nullable reference type variable) has been definitely assigned to a non-null value.
  • The variable or expression has been checked against null before de-referencing it.

The compiler generates warnings when you dereference a variable or expression that is maybe null in a nullable warning context. Furthermore, the compiler generates warnings when a non-nullable reference type is assigned to a maybe null variable or expression in an enabled nullable annotation context.

Examples

After we go through concepts relate to Nullable Context, Nullable Annotation Context, Nullable Warning Context. We should go to examples and see its warning.

The above code block with 2 green line at the “null” value and the “weatherForecast” variable will show warning messages as below.

We’re going to the next example about nullable reference type and the complier generates warning messages for it.

The nullable “weatherForecast” variable may be null in here, so that the compiler will show a warning message to you.

So with the same example like above, we will use Null Forgiving Operator (!) to ignore warnings for “weatherForecast” when it’s dereferencing to value of Summary field.

Then, the “Dereference of a possibly null reference” warning message won’t show to developer in this screnario.

Finally, the other example will show about making warning message as error when developer would like to see and fix it before building and running the program. We need to set up “Treat warnings as errors” settings in .csproj file as below:

And we also have an example like this:

The error messages for above example as below.

Recap

In this blog post, we have gone through and answer questions about What is the Nullable Reference Types? What benefits the Nullable Reference Types bring to? and have knowledge about Nullable Context, Nullable Annotation Context, Nullable Warning Context which relate to Nullable Reference Types.

Beside that we could see how to warnings and annotations show on examples make more clear for developers. Then we hope the blog post can help you avoid “billion-dollar” cost on “mistake” like Mr. Tony Hoare ever have gotten it 😀