Nullable Reference Types in C#

Nullable Reference Types in C# 8 with Examples

In this article, I am going to discuss Nullable Reference Types in C# 8 with Examples. Please read our previous article where we discussed Disposable Ref Structs in C# with Examples. Inside a nullable annotation context, any variable 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.

Nullable Reference Types in C#:

C# 8.0 allows us to specify whether a variable should be null, and when it cannot be null. Based on these annotations, the compiler will warn you when you are potentially using a null reference or passing a null reference to a function that will not accept it.

In C#, a reference type refers to an object that is created on the heap memory. When the reference type variable does not point to any object, then its value is null. Prior to C# 8.0, all reference types were nullable. Nullable reference types refer to a group of features introduced in C# 8.0 that you can use to minimize the likelihood that your code causes the runtime to throw NullReferenceException.

Note: Most programmers assumed that the reference type is meant to accept both null and non-null. There was not any explicit handling required and unfortunately, it is one of the reasons for NullReferenceException. In C# 8.0, nullable reference types and non-nullable reference types are introduced that enable us to make important statements about the properties of reference type variables.

Example to Understand Nullable Reference Types in C#:

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 before it’s accessed or assigned to a non-nullable reference type.

C# 8.0 introduces nullable reference types. This feature is another way to specify whether a given parameter, variable or return value can be null or not. In C# 8, the compiler emits a warning or error if a variable that must not be null is assigned to null. Those warnings can help you to find and fix most of your null exception bugs before they blow up at runtime.

Let us understand this with an example. Please have a look at the below example. In visual studio, you will see that we are not getting any warning.

using System;
namespace Csharp8Features
{
    public class NullableReferenceTypes
    {
        public static void Main()
        {
            string message = null;

            // warning: dereference null.
            Console.WriteLine($"The length of the message is {message.Length}");

            var originalMessage = message;
            message = "Hello, World!";

            // No warning. Analysis determined "message" is not null.
            Console.WriteLine($"The length of the message is {message.Length}");

            // warning!
            Console.WriteLine(originalMessage.Length);
        }
    }
}

The following image shows code in Visual Studio without warning.

Example to Understand Nullable Reference Types in C#

So, to show a warning we need to enable nullable reference types.

How to Enable Nullable Reference Types in C#?

Null-state analysis and variable annotations are disabled by default for existing projects meaning that all reference types continue to be nullable. Starting in .NET 6, they’re enabled by default for new projects.

In order to enable Nullable annotations in our project, we need to edit the project .csproj file and add <Nullable>enable</Nullable> in the property group as shown in the below image.

How to Enable Nullable Reference Types in C#?

At the end of this article, you will understand how this feature work to produce warnings when our code dereferences a null value. Dereferencing a variable means accessing one of its members using the dot (.) operator. For a better understanding, please have a look at the below code.

string message = “Welcome to Csharp.NET”;
int length = message.Length; // dereferencing “message”

Note: When you dereference a variable whose value is null, then you will get a runtime NullReferenceException.

The null-state analysis tracks the null-state of references. This static analysis emits warnings when your code may dereference null. You can address these warnings to minimize incidences when the runtime throws a NullReferenceException. The compiler uses static analysis to determine the null state of a variable. A variable is either not-null or maybe-null. The compiler determines that a variable is not-null in two ways:

  1. The variable has been assigned a value that is known to be not null.
  2. The variable has been checked against null and hasn’t been modified since that check.

Any variable that the compiler hasn’t determined as not-null is considered maybe-null. The analysis provides warnings in situations where you may accidentally dereference a null value. The compiler produces warnings based on the null state.

  1. When a variable is not-null, that variable may be dereferenced safely.
  2. When a variable is maybe-null, that variable must be checked to ensure that it isn’t null before dereferencing it.

For a better understanding, please have a look at the below code:

Nullable Reference Types in C# 8 with Examples

In the above example, the compiler determines that message is maybe null when the first message is printed. There’s no warning for the second message. The final line of code produces a warning because originalMessage might be null.

Complete Example Code:
using System;
namespace Csharp8Features
{
    public class NullableReferenceTypes
    {
        public static void Main()
        {
            string message = null;

            // warning: dereference null.
            Console.WriteLine($"The length of the message is {message.Length}");

            var originalMessage = message;
            message = "Hello, World!";

            // No warning. Analysis determined "message" is not null.
            Console.WriteLine($"The length of the message is {message.Length}");

            // warning!
            Console.WriteLine(originalMessage.Length);
        }
    }
}

Nullable state analysis and the warnings the compiler generates help you avoid program errors by dereferencing null. When you run the above code, you will get a runtime error. You are also getting a warning and that will help you to avoid this runtime exception. In the below example, we have handled the null exception.

using System;
namespace Csharp8Features
{
    public class NullableReferenceTypes
    {
        public static void Main()
        {
            string message = null;

            // warning: dereference null.
            if(string.IsNullOrEmpty(message))
            {
                Console.WriteLine($"The length of the message is {message.Length}");
            }
            
            var originalMessage = message;
            message = "Hello, World!";

            // No warning. Analysis determined "message" is not null.
            Console.WriteLine($"The length of the message is {message.Length}");

            // warning!
            Console.WriteLine(originalMessage.Length);
        }
    }
}

So, this new feature of C# 8 helps us to solve the NullReferenceException

Enable Nullable annotations in a file or part of the code

You can put the #nullable enable directive where you want to enable the functionality and the #nullable disable directive, where you want to disable the functionality.

If you put #nullable disable on the file head, that will not allow the nullability check for the whole file. For a better understanding, please have a look at the below image.

Enable Nullable annotations in a file or part of the code

If you put #nullable enable on the file head, that should allow the nullability check for the whole file. For a better understanding, please have a look at the below image.

Enable Nullable annotations in a file or part of the code

Finally, you can restore the default setting as below:
#nullable restore

Let us see a few more examples to get more clarity.

Example 1:
using System;
namespace Csharp8Features
{
    public class NullableReferenceTypes
    {
        public static void Main()
        {
            string? nullableString = null; // Is Ok, nullableString it can be null and it is null.
            Console.WriteLine(nullableString.Length); // WARNING: nullableString is null! Take care!
        }
    }
}
Example 2:
class Person
{
    public string Name { get; set; } // Warning normalString is null!
    public string? NullableName { get; set; }

    ////Enable the below code then the warning above will be disappeared
    //public Person(string name)
    //{
    //    Name = name;
    //}
}

The first property Name is a reference type, and it is null for this reason the compiler warning you. The Second property is NullableName is a nullable reference type that’s why the compiler is not warning because the NullableName can be null, you have defined it as nullable.

Benefits of Nullable Reference Types in C#

The introduction of this feature from version 8.0 allows for several benefits that are not present in earlier versions:

  1. Allows the programmer to clearly show their intent when declaring variables.
  2. Provides protection against null reference exceptions.
  3. The compiler warns you if you dereference a nullable reference when it may be null.
Rules for Non-nullable Reference Type in C#

When a variable is not supposed to be null, the compiler enforces some rules to make sure that it is safe to dereference that variable without checking that it is not null.

  1. The variable must be initialized to a non-null value.
  2. The variable can never be assigned the null value.
Rules for Nullable Reference Type in C#

When a variable can be null, in that case, the compiler enforces different rules to make sure that you have correctly checked for a null reference.

  1. The variable may only be dereferenced when the compiler can guarantee that the value is not null.
  2. It may be initialized with the default null value and may be assigned the value null in another code.

In the next article, I am going to discuss Asynchronous Streams in C# 8 with Examples. Here, in this article, I try to explain Nullable Reference Types in C# 8 with Examples. I hope you enjoy this Nullable Reference Types in C# with Examples article.

Leave a Reply

Your email address will not be published.