Telerik blogs
DotNetT Dark_870x220

The next major version release of the C# language comes with exciting new features. One of them is nullable reference type, which helps prevent the unhandled null reference exceptions that have ruined our apps at some point. Read along while I introduce you to this new feature.

C# 8 is the next major version release of the C# programming language. This has got people in the C# community excited because it comes with exciting new features, and I'm excited to share some of them with you. The plan is to release C# 8 at the same time as .NET Core 3.0, which is still in preview. I'll be publishing a couple of posts on C# 8 where I'll briefly introduce you to some of the features I'm excited about, so as to get you excited about them too.

In this article, I'll be writing about nullable reference types.

Installing .NET Core 3

.NET Core 3 is still in preview and didn't come with the release of Visual Studio 2019 on April 2. In order to create .NET Core 3.0 projects with VS2019, you should do the following:

  • Download .NET Core 3.0
  • Open Visual Studio
  • Click Tools > Options in the top menu
  • Expand Projects and Solutions | .NET Core
  • Select the Use previews of the .NET Core SDK checkbox and click OK to save.

With this setting, when you create a new project, you can select .NET Core 3.0 as the targeted framework. If you're using macOS or Linux, running dotnet new [project type] starts you off with .NET Core 3.0 as the targeted framework, which you can change from the project file if you want. To be sure that C# 8.0 preview features are available, you can set the <LangVersion /> property explicitly for your project. This setting is buried deep inside the Advanced settings within your project's Build tab. In order to update that, follow the instructions below:

  • Right-click your project in Solution Explorer.
  • Select Properties to view your project properties.
  • Click the Build tab within your project properties
  • Click the Advanced button on the lower right
  • Select C# 8.0 (beta) as the language version

This can also be set by editing the .csproj file and adding 8.0 as the value for <LangVersion> element.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <LangVersion>8.0</LangVersion>
  </PropertyGroup>
</Project>

Nullable Reference Types

In C#, one of the differences between value types and reference types is that value types are not nullable while reference types are nullable. This means that the statement int amount = null will result in a build error whereas Account savingsAccount = null is a valid syntax and the compiler compiles happily for you. It's possible to allow null for value types if you really need to. All you need to do is to adorn the type with ? or use the Nullable<T> type. So, int? amount = null becomes valid code and your app can compile without errors.

If you have been programming for a while, you might have experienced unhandled NullReferenceException exceptions a couple of times. This might happen for various reasons, and you have to put some kind of guards or checks to avoid encountering this exception. The nullable reference type that comes with C# 8 is designed to help avoid accidental null reference exceptions. The feature is designed to allow variable type definitions to specify whether they can have null assigned to them or not. So, by default, reference types are non-nullable and you have to use ? with the type to specify that it can accept null as a value. While this makes reference types non-nullable and stops you from assigning null as value to the variable, it does so with a warning which you see at compile time and not with errors that could fail the build.

Let's look at an example:

static void Main(string[] args)
{
  string firstName = "Jake ";
  string middleName = null; //warning CS8600: Converting null literal or possible null value to non-nullable type
  string lastName = " Archibald";

  PrintFullName(firstName, middleName, lastName); //warning CS8604: Possible null reference argument for parameter 'middle' in 'void Program.PrintFullName(string first, string middle, string last)'
}

static void PrintFullName(string first, string middle, string last)
{
}

The code snippet above will result in two warnings related to nullable reference, and the warnings are placed as comments in the example. The warnings point to middleName, which is assigned a value of null and passed as a parameter to PrintFullName method. What if the middle name is optional for the application? You can mark the variable type as nullable using the T? syntax.

static void Main(string[] args)
{
  string firstName = "Jake ";
  string? middleName = null;
  string lastName = " Archibald";
  PrintFullName(firstName, middleName, lastName);
}

static void PrintFullName(string first, string? middle, string last)
{
  Console.WriteLine(middle?.Length);
  Console.WriteLine(first + middle + last);
}

In the code above we used string? to mark the variable and parameter type as nullable, and running the application there won't be any warning. While T? syntax is used to denote nullable reference types, there isn't a long form for this. You might be thinking you can use Nullable<T> but it won't work, and only supports using the T? syntax.

Enable Nullable Reference Types

You can opt in to use this feature. It can be set at the project level or in a source file using #nullable and #pragma warning pre-processor directives. The nullable annotation context and nullable warning context determines whether nullable annotations have effect, and whether nullability warnings are given.

To enable this feature project-wide, you should add the <NullableContextOptions></NullableContextOptions> element to the first PropertyGroup element of the project file.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <NullableContextOptions>enable</NullableContextOptions>
  </PropertyGroup>
</Project>

If there is no project level setting, the default is for this feature to be disabled (i.e. for both nullable contexts to be disabled). The #nullable directive controls both the annotation and warning contexts, while the #pragma warning ... nullable directives control only the warning context, leaving the annotation context unchanged.

static void Main(string[] args)
{
  #nullable enable
  string firstName = "Jake ";
  string middleName = null; // warning CS8600: Converting null literal or possible null value to non-nullable type.
  string lastName = " Archibald";
  #nullable restore

  PrintFullName(firstName, middleName, lastName);
}

static void PrintFullName(string first, string? middle, string last) // warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context
{
}

In the code above, the nullable context is enabled for a few lines of code (assuming that it's disabled in the project setting), and the setting is restored to its previous context before we enabled it. If we had used #nullable disable it would have disabled it for the code that follows, which might not be what you want. Therefore, it's better to use restore and not disable. You should notice the warning it gives at the line the PrintFullName method was declared. This is because we used the syntax to create a nullable variable type while the feature is disabled.

For more info on nullable context and pre-processor directives, check out the feature specification.

That's A Wrap 🚀

C# 8 is the next major version release of the C# language, and it comes with exciting new features. I'll be sharing a couple more of them with you in some upcoming blog posts — I've talked about the nullable reference type in this post. The idea behind it is to allow developers to specify if a variable or parameter type definition should accept null. You can opt in to this feature and enable it in the project setting, or in a source file using some pre-processor directives.


Peter Mbanugo
About the Author

Peter Mbanugo

Peter is a software consultant, technical trainer and OSS contributor/maintainer with excellent interpersonal and motivational abilities to develop collaborative relationships among high-functioning teams. He focuses on cloud-native architectures, serverless, continuous deployment/delivery, and developer experience. You can follow him on Twitter.

Comments

Comments are disabled in preview mode.