Correctly Check and Use Nullable Variables in C#

The programming language C# has many ways to check variables for a null value. Some are more intuitive than others. My team and I have run into confusion with checking nullable C# types so here are the major ways of checking for null values and then potential stumbling blocks with using the value after it has been checked.

Ways to Check for Null

There are two primary ways to compare variables to a null or empty value. C# has the equality operator and the is operator. They are similar but C#’s is primarily for checking if a variable is compatible with a given type. I will provide definitions for both and will use “is” for the example code because checking null is checking the type of a variable.
Equality Operators

The operators == and != can be used to compare variables to the value null. This operator can be overloaded to support user defined structs. There is more detail in the Microsoft Docs but that is enough information to work with null.

Is Operator

The is operator checks if an expression is compatible with the provided type. For example checking


i is string

will check if the value of i can be cast to a string. The is operator can be used to check for specific variable values but I will focus on checking nullability.
Using the is operator guarantees that a user overloaded == or != operator will not be called.

Using Nullable Variables After Checking Them

For the examples below I will be passing various types into a function that is expecting a non-nullable version of that type. Then the compiler will provide an error if it believes the null check has not been performed correctly.

Here is the function that we will be passing variables into. The generic is there to make it easy to tell what type the function is expecting to be called with.


public static bool FunctionThatNeedsNonNullArguments(T i)
{
   return true;
}

Nullable Integers

When an int has been checked for null you must use the .value if the compiler expects a non nullable type.

For example the following code:


public static bool UseNullableVariable(int? i)
{
   if (i is null)
   {
       return false;
   }


   return FunctionThatNeedsNonNullArguments(i);
}

Gives the following compiler error:
Error CS1503 : Argument 1: cannot convert from ‘int?’ to ‘int’

I have already checked that the value of i is not null but the compiler doesn’t recognize that. In order for this code to compile I must get the value of the integer variable like so:


public static bool UseNullableVariable(int? i)
{
   if (i is null)
   {
       return false;
   }
  
   return FunctionThatNeedsNonNullArguments(i.Value);
}

This function compiles fine.

Using the == operator also has the same behavior.

Nullable Strings

Now, what happens if you have a nullable string?


public static bool UseNullableVariable(string? i)
{
   if (i is null)
   {
       return false;
   }


   return FunctionThatNeedsNonNullArguments(i.Value);
}

Now I get a different error:

Error CS1061 : 'string' does not contain a definition for 'Value’

How am I supposed to deal with this now? Well since the variable is now a string we can remove the .Value and the compiler is perfectly happy.

The following compiles:


public static bool UseNullableVariable(string? i)
{
   if (i is null)
   {
       return false;
   }


   return FunctionThatNeedsNonNullArguments(i);
}

What is going on here?

Defining a variable as the int? type Is actually creating an instance of a Nullable type. Int is a value type which means it cannot hold the value null. Clearing the value of an int results in the value 0. To get around this limitation the Nullable wrapper class adds a .HasValue and a .value property. The same null check can be written using both of these properties, like so:


public static bool UseNullableVariable(int? i)
{
   if (!i.HasValue)
   {
       return false;
   }


   return FunctionThatNeedsNonNullArguments(i.Value);
}

Now why doesn’t string need or have these properties?

It is because strings in C# are builtin reference types instead of value types. Reference types default to the value of `null` when no value is provided. This means that C# doesn’t need to add special handling to support a null value for these variable types.

You can find the full lists of value and reference types in the C# documentation:

C# reference types
C# value types

When checking nullability for a reference type, use the is operator and then pass the variable into whatever function needs it, like so:


public static bool UseNullableVariable(string? i)
{
   if (i is null)
   {
       return false;
   }


   return FunctionThatNeedsNonNullArguments(i);
}

Then, when working with a value type, use the is operator and then pass the value property into the function.


public static bool UseNullableVariable(int? i)
{
   if (i is null)
   {
       return false;
   }
  
   return FunctionThatNeedsNonNullArguments(i.Value);
}

When nullability in C# is enabled, it is important to check for and handle nulls properly. Keep in mind that different variables handle nulls differently, and it can save confusion and time fighting with the compiler.

Conversation

Join the conversation

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