Working with null values
You have now seen how to store primitive values like numbers in variables. But what if a variable does not yet have a value? How can we indicate that? C# has the concept of a null
value, which can be used to indicate that a variable has not been set.
Making a value type nullable
By default, value types like int
and DateTime
must always have a value, hence their name. Sometimes, for example, when reading values stored in a database that allows empty, missing, or null values, it is convenient to allow a value type to be null
. We call this a nullable value type.
You can enable this by adding a question mark as a suffix to the type when declaring a variable. Let's see an example:
- In the
Chapter02
folder, create a new folder namedNullHandling
. - Add the
NullHandling
folder to theChapter02
workspace. - Create a new Terminal window for the
NullHandling
project. - Create a new console application project in the
NullHandling
folder. - Select
NullHandling
as the current project for OmniSharp. - In the
NullHandling
project, inProgram.cs
, in theMain
method, add statements to declare and assign values, includingnull
, toint
variables, as shown in the following code:int thisCannotBeNull = 4; thisCannotBeNull = null; // compile error! int? thisCouldBeNull = null; Console.WriteLine(thisCouldBeNull); Console.WriteLine(thisCouldBeNull.GetValueOrDefault()); thisCouldBeNull = 7; Console.WriteLine(thisCouldBeNull); Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
- Comment out the statement that gives a compile error.
- Run the application and view the result, as shown in the following output:
0 7 7
The first line is blank because it is outputting the null
value!
Understanding nullable reference types
The use of the null
value is so common, in so many languages, that many experienced programmers never question the need for its existence. But there are many scenarios where we could write better, simpler code if a variable is not allowed to have a null
value.
More Information: You can find out more through the following link, where the inventor of null
, Sir Charles Antony Richard Hoare, admits his mistake in a recorded hour-long talk: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
The most significant change to the language in C# 8.0 was the introduction of nullable and non-nullable reference types. "But wait!", you are probably thinking, "Reference types are already nullable!"
And you would be right, but in C# 8.0 and later, reference types can be configured to no longer allow the null
value by setting a file- or project-level option to enable this useful new feature. Since this is a big change for C#, Microsoft decided to make the feature opt-in.
It will take multiple years for this new C# language feature to make an impact since there are thousands of existing library packages and apps that will expect the old behavior. Even Microsoft has not had time to fully implement this new feature in all the main .NET 5 packages.
More Information: You can read the tweet about achieving 80% annotations in .NET 5 at the following link: https://twitter.com/terrajobst/status/1296566363880742917
During the transition, you can choose between several approaches for your own projects:
- Default: No changes are needed. Non-nullable reference types are not supported.
- Opt-in project, opt-out files: Enable the feature at the project level and, for any files that need to remain compatible with old behavior, opt out. This is the approach Microsoft is using internally while it updates its own packages to use this new feature.
- Opt-in files: Only enable the feature for individual files.
Enabling nullable and non-nullable reference types
To enable the feature at the project level, add the following to your project file:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
To disable the feature at the file level, add the following to the top of a code file:
#nullable disable
To enable the feature at the file level, add the following to the top of a code file:
#nullable enable
Declaring non-nullable variables and parameters
If you enable nullable reference types and you want a reference type to be assigned the null
value, then you will have to use the same syntax as making a value type nullable, that is, adding a ?
symbol after the type declaration.
So, how do nullable reference types work? Let's look at an example. When storing information about an address, you might want to force a value for the street, city, and region, but the building can be left blank, that is, null:
- In
NullHandling.csproj
, add an element to enable nullable reference types, as shown highlighted in the following markup:<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net5.0</TargetFramework> <Nullable>enable</Nullable> </PropertyGroup> </Project>
- In
Program.cs
, at the top of the file add a statement to enable nullable reference types, as shown in the following code:#nullable enable
- In
Program.cs
, in theNullHandling
namespace, above theProgram
class, add statements to declare anAddress
class with four fields, as shown in the following code:class Address { public string? Building; public string Street; public string City; public string Region; }
- After a few seconds, note that the C# extension warns of problems with non-nullable fields like
Street
, as shown in the following screenshot:Figure 2.6: Warning messages about non-nullable fields in the PROBLEMS window
- Assign the empty string value to each of the three fields that are non-nullable, as shown in the following code:
public string Street = string.Empty; public string City = string.Empty; public string Region = string.Empty;
- In
Main
, add statements to instantiate anAddress
and set its properties, as shown in the following code:var address = new Address(); address.Building = null; address.Street = null; address.City = "London"; address.Region = null;
- Note the warnings, as shown in the following screenshot:
Figure 2.7: Warning message about assigning null to a non-nullable field
So, this is why the new language feature is named nullable reference types. Starting with C# 8.0, unadorned reference types can become non-nullable, and the same syntax is used to make a reference type nullable as is used for value types.
More Information: You can watch a video to learn how to get rid of null reference exceptions forever at the following link: https://channel9.msdn.com/Shows/On-NET/This-is-how-you-get-rid-of-null-reference-exceptions-forever
Checking for null
Checking whether a nullable reference type or nullable value type variable currently contains null
is important because if you do not, a NullReferenceException
can be thrown, which results in an error. You should check for a null
value before using a nullable variable, as shown in the following code:
// check that the variable is not null before using it
if (thisCouldBeNull != null)
{
// access a member of thisCouldBeNull
int length = thisCouldBeNull.Length; // could throw exception
...
}
If you are trying to use a member of a variable that might be null
, use the null-conditional operator ?.
, as shown in the following code:
string authorName = null;
// the following throws a NullReferenceException
int x = authorName.Length;
// instead of throwing an exception, null is assigned to y
int? y = authorName?.Length;
More Information: You can read more about the null-conditional operator at the following link: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators
Sometimes you want to either assign a variable to a result or use an alternative value, such as 3
, if the variable is null
. You do this using the null-coalescing operator, ??
, as shown in the following code:
// result will be 3 if authorName?.Length is null
var result = authorName?.Length ?? 3;
Console.WriteLine(result);
More Information: You can read about the null-coalescing operator at the following link: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator