C# Nullable Type Tutorial

In this tutorial we learn how to use the generic nullable type in C# to allow value types to accept a null value.

We cover the nullable shorthand, null checks with HasValue, Value and GetValueOrDefault(), nullable class members, the null coalescing operator (??) and how to use the null coalescing operator with methods.

What is a nullable type

In the data type tutorial we learned that value types cannot have null values assigned to them.

But, sometimes we need value types such as int to be able to accept null as a value. That’s where the generic nullable type comes in handy.

To allow a value type variable to have a null value, we use the keyword Nullable , followed by the type we want between angle brackets.

Syntax:
Nullable<value_type> variable_name = null;
Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            Nullable<int> num = null;

            if(num != null)
                Console.WriteLine(num);
            else
                Console.WriteLine("Null value");

            Console.ReadLine();
        }
    }
}

In the example above, we create a variable of type int which is not nullable by default. But, because we use the Nullable generic, it can accept null as a value.

Nullable shorthand

You probably noticed in the example above that Visual Studio wanted to simplify the nullable initialization.

The shorthand for a nullable type is the ? operator written after the type.

Syntax:
value_type? variable_name = null;
Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int? num = null;

            if (num != null)
                Console.WriteLine(num);
            else
                Console.WriteLine("Null value");

            Console.ReadLine();
        }
    }
}

In the example above, we create a variable of type int again. This time we use the shorthand ? operator instead of the longer Nullable generic syntax.

Nullable null checks with the HasValue and Value properties

Until now we’ve been using the != null expression to check if the type is null. But, C# actually makes two properties available to us to perform null checks, .HasValue and .Value .

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int? num = null;

            if (num.HasValue)
                Console.WriteLine(num.Value);
            else
                Console.WriteLine("Null value");

            Console.ReadLine();
        }
    }
}

.HasValue is a boolean property that checks if the value is null or not. .Value is the property to access the value of the nullable.

.Value will throw a InvalidOperationException if the value is null, otherwise, it will return the value.

Nullable null checks with the GetValueOrDefault() function

We also have access to the GetValueOrDefault() method that will check if the value exists. If the value is null, the method will return the default value for that type.

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int? num = null;
            Console.WriteLine(num.GetValueOrDefault());

            num = 10;
            Console.WriteLine(num.GetValueOrDefault());

            Console.ReadLine();
        }
    }
}

In the first function call, the value printed to the console is 0, which is the default for an int. In the second function call, the variable has a value, so the value is printed instead.

Nullable as class members

Class members (attribute fields and methods) can be nullable types.

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            NullMembers nullMembers = new NullMembers();

            Console.WriteLine(nullMembers.num.GetValueOrDefault());

            nullMembers.num = 10;
            Console.WriteLine(nullMembers.num);

            Console.ReadLine();
        }
    }

    class NullMembers
    {
        public int? num { get; set; }
    }
}

In the example above, we only use a nullable attribute field, but class methods can be nullable as well.

Nullable as a method parameter

If we want a nullable as a method parameter, we add a ? operator after the parameter type.

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            Square(null);
            Square(5);

            Console.ReadLine();
        }

        static void Square(int? num)
        {
            num = num.GetValueOrDefault();
            Console.WriteLine(num + " squared = " + num * num);
        }
    }
}

In the example above, we do a GetValueOrDefault() check before the calculation, because the parameter could be null.

Nullable as a method return type

To allow a method to return a nullable type, we add the ? operator after the return type.

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine(Square(5));
            Console.WriteLine(Square(null).GetValueOrDefault());

            Console.ReadLine();
        }

        static int? Square(int? num)
        {
            return num * num;
        }
    }
}

In the example above, we do a GetValueOrDefault() check on the method because it could return a null.

The null coalescing operator (??)

If we wanted to assign the value of a nullable variable to a variable that is not nullable, the compiler would not allow it.

Example:
namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int? nullNum = null;
            int nonNullableNum = nullNum; // compiler error
        }
    }
}

The variable nullNum is nullable and has a value of null. The variable nonNullableNum is a normal int variable and so cannot accept a null value.

When we try to assign nullNum (null value) to the normal int, the compiler will raise an error.

So we have to use the null coalescing ?? operator to set a default value to be used in case the nullable variable’s value is null.

Syntax:
normal_type name = nullable_variable ?? default_value;
Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int? nullNum = null;
            int nonNullableNum = nullNum ?? 10;

            Console.WriteLine(nonNullableNum);
            Console.ReadLine();
        }
    }
}

Which is the same as writing a null check:

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int? nullNum = null;
            int nonNullableNum = 0;

            if (nullNum != null)
                nonNullableNum = nullNum;
            else
                nonNullableNum = 10;

            Console.WriteLine(nonNullableNum);
            Console.ReadLine();
        }
    }
}

The null coalescing operator with methods

The null coalescing operator works with methods that have a nullable return type.

Example:
using System;

namespace NullableTypes
{
    class Program
    {
        static void Main()
        {
            int nonNullableNum = NullMethod() ?? 10;

            Console.WriteLine(nonNullableNum);
            Console.ReadLine();
        }

        static int? NullMethod()
        {
            return null;
        }
    }
}

In the example above, we specify that we want 10 to be the value if the method returns null.

Summary: Points to remember

  • A nullable type is a generic class that allows value types to accept a null value.
  • The nullable shorthand is written as the ? operator after the type.
  • We can perform null checks with the Value and HasValue properties or the GetValueOrDefault() method.
    • The GetValueOrDefault() method allows us to provide a default value if a null is found.
  • The nullable type can be used with class members, method parameters and as method return types.
  • The null coalescing operator (??) can set a default value to be used in case a nullable’s value is null.
    • The ?? is written after the value, followed by the value we want as default.