Safer Nullability in Modern C#
Tags: csharp, dotnet, beginners
Published: 2023-02-25 20:04:04

---
title: Safer Nullability in Modern C#
published: true
description: Exploring nullability improvements in C# and the !, ?, ??, and ??= Operators
tags: CSharp, dotnet, beginners
canonical_url: https://newdevsguide.com/2023/02/25/csharp-nullability/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ju1dp86mivpfnjmgywh2.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-02-25 19:57 +0000
---

`Null` is famously quoted as being [the “billion-dollar mistake”](https://en.wikipedia.org/wiki/Tony_Hoare) due to the quantity of `NullReferenceException`s that we see in code (particularly when just starting out).  
  
The prevalence of `null` forces a significant amount of developer attention doing things like:

- Validating that parameters are not `null`
- Writing conditional logic to prevent `NullReferenceException`s from occurring
- Defaulting variables to other values when `null` values are encountered

Thankfully, a few years ago dotnet gave us some additional tools to detect and counteract `null` values at compile time, to compete with similar features in languages like F# and others.

In this article I’ll walk through some of the nullability options we have when writing C# code as well as some of the supporting syntax that is handy when working with potentially null values.

## Enabling Project-wide Nullable Checks

C# 8 introduced a radically different way of enforcing null reference exceptions at compile time using the project-wide nullable context.

This is an opt-in setting that will highlight potential null reference exceptions in your code during development. This helps you identify potential `NullReferenceException`s before your program even runs.

My personal opinion is that I am almost always in favor of a stricter compiler if it can help me avoid bad code at runtime, but you may find this not as much to your liking.

In order to enable this setting, right click on your project in Visual Studio and then select “Properties”.

Once you are in properties, find the “General” blade under the “Build” menu and then change the “Nullable” setting to “Enable”. This will turn on project-wide nullable checks at compile time for that project.

![Enabling C# Nullability Analysis](https://i0.wp.com/newdevsguide.com/wp-content/uploads/2023/02/image-6.png?resize=711%2C302&ssl=1)

Checking this modifies your `.csproj` file and sets `Nullable` to “`enable`” within your `PropertyGroup` element.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>
```

Once this setting is enabled, potential `NullReferenceException`s are highlighted in the Visual Studio editor surface as shown below:

![A warning on nullability in C# code](https://i0.wp.com/newdevsguide.com/wp-content/uploads/2023/02/image-7.png?resize=883%2C459&ssl=1)

Here we see Visual Studio pointing out that `_context` may currently be null given what it knows about your code. This indicates that a `NullReferenceException` *may* occur when this line runs.

These warnings will also appear in your output from building the project and in your error list if you include Build + IntelliSense errors in that display as shown in the image above.

It’s worth noting that project-wide null checks are only available in C# 8 and later versions.

## The Nullable Operator (?)

Once you turn on project-level null checks, all reference variables are assumed to be non-null unless you explicitly tell C# that `null` values are possible in that variable.

We tell C# that `null` are allowable in a variable using the nullable operator (?) as shown with the following declaration:

```csharp
// _context may have a null value after initialization
private GameContext? _context;

// _graphics cannot have a null value after initialization
private GraphicsDeviceManager _graphics;
```

When you add the `?` operator after a Type declaration, you tell the C# compiler that a `null` value is possible after the object has been instantiated. C# will then warn if you attempt to reference the value without checking for null.

If you omit the `?` operator, C# will assume that once the object is instantiated, null values are not possible in that member.

Thankfully, C# will also warn you if it is possible for something to be `null` when it is declared as non-nullable.

![A warning on not initializing a non-null field](https://i0.wp.com/newdevsguide.com/wp-content/uploads/2023/02/image-8.png?resize=561%2C211&ssl=1)

See my articles on [the required keyword](https://newdevsguide.com/2022/11/12/csharp-11-required-keyword/) and [init-only members](https://newdevsguide.com/2022/10/22/comparing-init-only-get-only-and-readonly-in-c-code/) for more options for ensuring that values are provided at time of object initialization in more recent versions of C#.

## The Non-Null Assertion (!)

While these null checks can be phenomenally helpful, they can sometimes miss the full context of your code and assume things are nullable that you know cannot be.

When this occurs, you can hint to C# that a variable is not null by adding the [null-forgiving operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/nullable-reference-types-specification#the-null-forgiving-operator) (`!)` to the end of the variable reference as shown below:

```csharp
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Black);

    // _context will always be initialized before draw is called
    _context!.Sprites.Begin();
    _context.Update(gameTime, true);
    _context.Sprites.End();

    base.Draw(gameTime);
}
```

Here we add `!` after the `_context` variable is referenced the first time. C# now knows to suppress its nullability warning for this variable for this line. Since `_context` has already been referenced before the next two lines run, they know a `NullReferenceException` will never occur on the following two lines. The result of this is that `!` effectively mutes false-positives for that variable for that method.

Note that if I am mistaken and I add `!` to something that actually *is* `null` at runtime, I will get a `NullReferenceException` when that line runs.

To me, having to add `!` in places is the most irritating aspect of the C# nullability improvements.

I’m finding that the irritation of using `!` is subtly changing my coding style slightly to ensure that non-null things remain non-null at the end of initialization. Honestly, I think this shift in design philosophy is a key benefit of turning on nullability analysis.

By enforcing nullability warnings and irritating me with having to use the `!` operator, C# is pushing me towards better runtime code quality.

## Null-Conditional Operator (?)

Finally, let’s look at the `?` and `??` operators that help you safely deal with `null` without having to use a lot of additional `if` statements.

Without these operators, if you wanted to safely call something on a variable that might be null, you had to write an `if` statement as shown below:

```csharp
if (_context != null)
{
    _context.Update();
}
```

However, with the `?` operator, you can condense this down into a single line of code:

```csharp
_context?.Update();
```

This will invoke `Update` on `_context` if `_context` is not currently `null`. If `_context` *is* `null`, nothing will happen and the program will advance to the next statement.

See [Microsoft’s documentation on the Null-conditional operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-) for more information.

## Null Coalescing Operator (??)

The `??` operator is somewhat related. If you wanted to initialize a variable to one variable if it isn’t `null` or a fallback value if it *is* `null`, you’d have to write some code like the following:

```csharp
string displayText;

if (value != null)
{
    displayText = value;
}
else
{
    displayText = "Fallback value";
}
```

Now, you could write this with a ternary operator as `string displayText = value != null ? value : "Fallback value"`, but ternary operators are fairly hard to read, even if you’re familiar with them.

Instead, we have the [null coalescing operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator) that lets us write the code as the following:

```csharp
// use Fallback Value if value is null
string displayText = value ?? "Fallback value"; 
```

While this may be hard to read the first time you see it, I find this significantly more readable over time than the often-maligned ternary operator.

## Null Coalescing Assignment Operator (??=)

Because the `??` operator caught on so nicely, and developers like concise code, we now have a related [null coalescing assignment operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/null-coalescing-assignment) that helps us simplify code.

If you wanted to assign a value to a variable if the variable is currently null, you could write it as follows:

```csharp
if (displayText == null)
{
    displayText = "Fallback value";
}
```

Here we are simply assigning a value if something is currently null in a few lines of code.

The null coalescing assignment operator lets us do this in a single statement:

```csharp
// Assign "Fallback value" to displayText only if displayText is currently null
displayText ??= "Fallback value";
```

I find the null-coalescing assignment operator a bit harder to read than the other operators I mention in this article, but even it isn’t bad and you’re not required to use it.

## Final Thoughts

In this article I outlined how C# 8 and beyond allow you to use more advanced `null` checking at compiler time and give you additional language features to help deal with the issues that can arise out of `null` values and assignment statements.

As with any new language feature, these benefits of additional null safety come with the price of additional complexity in the form of new compiler warnings and new operators in our code. This added complexity can slow down and cause anxiety in new developers approaching the C# language.

However, the added benefits of early detection of null values and support for null-safe code patterns likely make up for the added complexity. Still, teams should be cautious about their use of any new language features and pick only the ones that give them the greatest value, knowing that they will need to teach new developers the meaning of some of these ever-increasing operators present in the C# language.