Nullable Types in C# (Explained Simply)

For decades developers have fought over null types. What do they mean? Why is there a silly little question mark in my C# code? What is consciousness? These are all topics that we will tackle in this blog post.

Prerequisite

In order to correctly understand nulls you must know the difference between a reference type and a value type.

//These are reference types
string s = null;
Object obj = null;

//These are value types
int i = 1;
bool test = true;

The important thing to realize is that value types cannot be null. This isn’t a fancy compiler trick or something unique to C#. This is everywhere in computer programming.

Remember that reference types are often “big” data types. Objects are stored as references, because they get so big that developers gave them their own space ie. “the heap”.

In fact, if you try to assign a null to a value type (aka “small” data type), you will get an error. Instead of assigning null, we put a zero for numbers.

Underneath the hood, null represents all zeros in memory which is default value for all reference type.

Nullable types aka “the question mark”

To represent null values in C#, we use a special character called a nullable type. It is often times represented as:

int? i = null;
Console.WriteLine(i == null);

We basically just told the compiler “give this value type the ability to be null”. The compiler will now give value type null capabilities.

Reference types do support being null, but do not support allowing null.

Shawn Wildermuth

The above quote essentially means, C# will allow you to add nullability but the linter/compiler will still give you errors unless you turn them off globally or with file declarations. This is unique to .NET and something that will cause much frustration to those not familiar.

But why allow nulls? Isn’t having nulls an anti-pattern?

If you are a Clean Code zealot, yes. But if you look in any codebase, you will find nulls everywhere and they are unavoidable. Almost all Entity Framework reads return nulls, so this will cause Clean Code people to rage (too my much delight!).

Underneath the hood

Often times when a coding construct is used frequently, C# does us a solid and makes it easier to use.

In essence the question mark is a fancy abstraction for Nullable<>. Our code from before translate to this:

Nullable<int> i = new Nullable<int>();

Nullable<> is actually a struct that has two fields that represent Value and HasValue.

public struct Nullable<T> where T : struct
{
     public T Value {get;}
     public bool HasValue {get;}
     ...
}

But why would we even do this to a value type? So we can check for nullability!

int? x = null

if(x.HasValue) Console.WriteLine("this is not null);

The above works, but we could also do this:

int? x = null

if (x is not null) Console.WriteLine("this is not null");

Bang Operator aka “Null-Forgiving Operator”

One piece of syntax that commonly coincides with the question-mark operator is often the “bang operator”. They look very similar but are very different.

The bang operator is a way of telling the compiler to ignore everything and treat it as non-null.

You commonly use the bang-operator to suppress null warnings. This is what a null warning looks like:

Warning CS8625: Cannot convert null literal to non-nullable reference type

Simply put, the bang operator makes this error go away!

Conslusion

Although the the null and bang operator look very similar, they do completely different things. Understanding the difference will help you immensely when diagnosing errors in you code.

Happy coding!

Posted in C#

Leave a Reply

Your email address will not be published. Required fields are marked *