Nullable Reference Types In C# 8

Getting Setup With C# 8

If you aren’t sure if you are using C# 8, or you know you aren’t and want to know how to access these features. Read this quick guide on getting setup with .NET Core and C# 8.

Current State Of Play

So first off.. Nullable reference types? But.. We’ve been taught for the past 17 years that a big part of “reference” types is that they are nullable. That’s obviously a simplification, but a junior programmer would typically tell you that “value type” => Not Nullable and “reference type” => Nullable.

Infact we have syntax to tell us whether a value type can actually be nullable, for example :

int? nullableInt1 = null;
Nullable<int> nullableInt2 = null;
int nullableInt3 = null; //Compile error

And remember this syntax isn’t just for primitive value types. For example this works just as well :

struct MyStruct
{

}
        
static void Main(string[] args)
{
    MyStruct? mystruct1 = null;
    MyStruct myStruct2 = null;
}

But now we want to have a piece of syntax to say that this should be a compile time error (Or warning)? Interesting.

class MyClass
{

}
        
static void Main(string[] args)
{
    MyClass myClass = null; //Acceptable by today's standards. No problem-o
}

Why?

So the first question to ask is. Why do we need this? Consider a program like so :

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}
        
static void Main(string[] args)
{
    var myClass = new MyClass();

    //100 lines of code. 
    myClass.SayHello();
}

Fairly simple. Notably I’ve left a comment to illustrate that maybe this program is a lot bigger than shown here, but everything should run fine and dandy. Now let’s say after some time, another developer comes along and does something like so  :

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}
        
static void Main(string[] args)
{
    var myClass = new MyClass();

    //100 lines of code. 
    if (true) //Some condition. 
    {
        myClass = null;
    }

    //100 lines of code. 
    if(myClass == null)
    {
        //Do something special here. 
    }
    //100 lines of code
    myClass.SayHello();
}

It may seem silly at first, but I’ve seen it happen. Someone has come along and set myClass to null to satisfy something they are working on. It’s buried deep in the program, and maybe within their tests, everything looks good.

But at some point, maybe with the perfect storm of conditions, we are going to throw a dreaded NullReferenceException. Naturally, we are going to do something like this and wrap our method call in a null check :

static void Main(string[] args)
{
    var myClass = new MyClass();

    //100 lines of code. 
    if (true) //Some condition. 
    {
        myClass = null;
    }

    //100 lines of code. 
    if(myClass == null)
    {
        //Do something special here. 
    }

    //100 lines of code. 
    if (myClass != null)
    {
        myClass.SayHello();
    }
}

But if I’m the original developer I may say to myself… I never intended this thing to ever be null. But it would be fruitless to say that I never want anything to be set to null. Ever. We would probably break that rule on day 1. But at the same time, are we expecting to code so defensively that we do a null check everytime we use a reference type?

And this is the crux of the nullable reference type. It’s about signally intent in code. I want to specifically say “I expect this to be nullable at some point” or vice versa so that other developers, future myself included, don’t fall into a null reference trap.

Turning On Nullable Reference Types

As noted above, the first thing is that you need to be using C# 8 to turn on the Nullable Reference Types feature. Once that’s done, you need to add a single line to your project’s csproj file :

<Nullable>enable</Nullable>

Note that earlier versions of the SDK required the following instead, so try the above and if you aren’t seeing what you expect, try below!

<NullableReferenceTypes>true</NullableReferenceTypes>

Warnings To Expect

Once we’ve turned on the feature, let’s look at a simple piece of code to illustrate what’s going on.

class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }
}

static void Main(string[] args)
{
    MyClass myClass = null;
    myClass.SayHello();
}

Building this we get two Warnings. I put that in bold because they are Warnings and not compile errors. Your project will still build and run, but it’s just going to warn you about shooting yourself in the foot. The first warning is for us trying to assign null to a variable that isn’t explicitly set to allow nulls.

Converting null literal or possible null value to non-nullable type.

And the second warning is when we are trying to actually use the non-nullable type and the compiler thinks it’s going to be null.

Possible dereference of a null reference.

So both of these aren’t going to stop our application from running (yet, more on that later), but it is going to warn us we could be in trouble.

Let’s instead change our variable to be nullable. I was actually a little worried that the syntax would be reversed. And instead of adding something extra to say something is nullable, I thought they might try and say all reference types are nullable by default, but you can now wrap it in something like NotNullable<MyClass> myClass… . But thankfully, we are using the same syntax we use to mark a value type as nullable :

static void Main(string[] args)
{
    MyClass? myClass = null;
    myClass.SayHello();
}

Interestingly, if we try and compile this, we still get the Possible dereference  warning! So now it’s not just about hey this thing is not supposed to be nullable, but you are assigning null, but it’s proactively warning us that hey, you aren’t actually checking if this thing is null and it very well could be. To get rid of it, we need to wrap our stock standard null check.

static void Main(string[] args)
{
    MyClass? myClass = null;
    if (myClass != null)
    {
        myClass.SayHello();
    }
}

And just like that, our warnings disappear.

Compiler Warning Limits

The thing is, reference types can be passed around between methods, classes, even entire assemblies. So when throwing up the warnings, it’s not foolproof. For example if we have code that looks like :

class MyClass
{
    public Random Random = new Random();
}


static void Main(string[] args)
{
    MyClass myClass = new MyClass();
    SomeMethod(myClass);
    var next = myClass.Random.Next(1, 10);
}

static void SomeMethod(MyClass myClass)
{
    myClass.Random = null;
}

We do get a warning that we are assigning null. But we don’t get the Possible Dereference warning. This we can assume that once the object is passed outside the method, whatever happens outside there (Like setting null), we aren’t going to be warned about. But if we assign null so blatantly in the same block of code/method, and then try and use it, then the compiler will try and give us a helping hand.

For comparisons sake, this does generate a warning :

static void Main(string[] args)
{
    MyClass myClass = new MyClass();
    if (new Random().Next(1, 10) > 5)
    {
        myClass = null;
    }

    myClass.SayHello();
}

So it’s not just about whether it’s certain, but just whether it’s atleast possible within the current code block.

Nullable Reference Types On Hard Mode

Not happy with just a warning. Well you can always level up and try nullable reference types on hard mode! Simply add the following to your project’s csproj file :

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

Note that this will treat *all* warnings as errors, not just ones regarding null reference issues. But it means your project will no longer compile if there are warnings being thrown up!

Final Note

There is so much in here to like, I thought I would just riff off a few bullet points about how I feel about the change.

  • This reminds me so much of the In Keyword that was introduced in C# 7.2. That had a few performance benefits, but actually the main thing was about showing the “intent” of the developer. We have a similar thing here.
  • Even if you aren’t quite sure on the feature, it’s almost worth banging it on and checking which Possible dereference  warnings get thrown up on an existing project. It’s a one line change that can easily be toggled on and off.
  • I like the fact it’s warnings and not errors. It makes it so transitioning a project to use it isn’t some mammoth task. If you are OK with warnings showing up over the course of a month while you work through them ofcourse!
  • There was rumblings of this feature being on by default when you start a new project in C#. That didn’t happen this release, but it could happen in the future. So it’s worth atleast understanding how things work because you very well may end up on a project in the future that uses it.

What do you think? Drop a comment below and let me know!

2 thoughts on “Nullable Reference Types In C# 8”

  1. The “it’s not foolproof” code sample is incorrect. It should use a “ref” modifier. Otherwise, it just sets the passed parameter to null, leaving the original value unchanged.

    Reply
    • Oof, You are correct! I was actually checking if it would incorrectly throw up a warning and copied and pasted the wrong piece of code.

      But the text around the code is correct! It doesn’t warn if you change a reference variable inside a method. You only get the null assignment warning, and not the warning about dereference.

      Reply

Leave a Comment