C# – Null-Conditional Operators (?. and ?[])

The System.NullReferenceException: Object reference not set to an instance of an object. exception is a common exception in C#. This exception is thrown when a variable of reference type is null and it is attempted to be accessed. This error can be avoided by using if conditions to make sure the variable is not null before accessing it. However, using the null-conditional operators can also prevent this error while creating shorter and easier to read code.

There are two types of null-conditional operator:

  • Member Access Operator: ?.
  • Element Access Operator: ?[]

This article will explain how to use the null-conditional operators and compare them to using if conditions.

A Simple Example Of The Member Access Operator (?.)

Lets start with a simple example that will throw the null reference exception.

string str1 = null;
string str2 = str1.ToUpper();   // Throws exception during runtime.

The above code will create the following error during runtime: Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. This is because the str1 variable is null and the ToUpper() method can not be called on null.

This code can be rewritten like below with an if condition to check if the str1 variable is null before attempting to call the ToUpper() method on it.

string str1 = null;
string str2 = null;
if (str1 != null)
{
    str2 = str1.ToUpper();
}

The above code can be rewritten like below using the null-conditional member access operator (?.).

string str1 = null;
string str2 = str1?.ToUpper();

A null-conditional operator will check the operand to the left and if it is null, then null is returned without continuing to evaluate the rest of the line of code. If the operand has a value, then the line of code will continue to be evaluated. In the above example, the str1 variable is to the left of the null-conditional operator (?.) so before running the ToUpper() method, the value of str1 is checked to find if it is null. The str1 variable is null, so null is returned and the ToUpper() method is never ran which prevents the null reference exception from occurring during run time. If str1 had a value like “test”, then the null-conditional operator would have checked that str1 was not null and the ToUpper() method would have been ran to return “TEST”.

An Example Of Accessing A Property

The code below shows an example of the null reference exception being thrown when a property of an object is accessed. The fname property is null, so when the ToUpper() method is attempted to be ran on fname the exception is thrown. This exception would also be thrown if the person1 variable was null.

using System;

namespace NullConditionalExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person1 = new Person();
            // Throws exception during run time.
            Console.WriteLine($"first name: {person1.fname.ToUpper()}");  
        }
    }

    class Person
    {
        public string fname { get; set; }
        public string lname { get; set; }
    }
}

The code below is an example of rewriting the above code to avoid an exception by checking if the person variable is null and then checking if the fname property is null before accessing person1.fname.

using System;

namespace NullConditionalExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person1 = new Person();
            if (person1 != null && person1.fname != null)
            {
                string fname = person1.fname.ToUpper();
                Console.WriteLine($"person1 first name: {fname}");
            }
            else
            {
                Console.WriteLine("person1 first name: ");
            }
        }
    }

    class Person
    {
        public string fname { get; set; }
        public string lname { get; set; }
    }
}

This is the output of the above code:

The below code uses the null-conditonal operator to check if person1 and person1.fname are null. The code below is shorter making it easier to read and easier to write so it has less chances of bugs.

In the statement person1?.fname?.ToUpper(), the first null-conditional operator (?.) checks the operand to its left which is the person1 instance. In this case, person1 is not null so the next null-conditional operator (?.) checks the operand to its left which is the person1.fname instance. In this case, person1.fname is null, so null is returned and the ToUpper() method is not ran on person1.fname preventing a null reference exception.

using System;

namespace NullConditionalExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person1 = new Person();
            string fname = person1?.fname?.ToUpper();
            Console.WriteLine($"person1 first name: {fname}");
        }
    }

    class Person
    {
        public string fname { get; set; }
        public string lname { get; set; }
    }
}

This is the output from the above code:

Chaining Null-Conditional (?.) And Null-Coalescing (??) Operators

The next example continues to show how much the null-conditional operator simplifies code and also shows chaining the null-conditional and null-coalescing operators. The null-coalescing operator is used to check if a variable has a value and if it does not then a default value is returned. The previous blog post has more information about the null-coalescing operator (??).

using System;

namespace NullConditionalExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person1 = new Person();
            
            // Chaining null-conditional (?.) and null-coalescing (??) operators
            string fatherName = person1?.father?.fname?.ToUpper() ?? "Missing Data";
            Console.WriteLine($"father name: {fatherName}");
        }
    }

    class Person
    {
        public string fname { get; set; }
        public string lname { get; set; }
        public Father father { get; set; }
    }

    class Father
    {
        public string fname { get; set; }
        public string lname { get; set; }
    }
}

This is the output from the above code:

In the above example, the following line of code uses the null-conditional and null-coalescing operators.

string fatherName = person1?.father?.fname?.ToUpper() ?? "Missing Data";

In this code, the first ?. checks the person1 operand to its left. The person1 variable has a value, so the next ?. checks the father operand to its left. The person1.father property is null. Now the line person1?.father?.fname.ToUpper() evaluates to null. With the null-coalescing ?? operator being used the next operand of “Missing Data” is evaluated and because it is a string that has a value “Missing Data” is returned and assigned to the fatherName variable.

Null-Conditonal – Element access operator – ?[]

The second null-conditional operator is the element access operator (?[]). The element access operator is used to prevent the null reference exception when accessing an element from a collection.

The below code will create the null reference exception error when running the statement Console.WriteLine(strList[0]); because the strList is null so the first element of null can not be accessed.

using System;
using System.Collections.Generic;

namespace NullConditionalExample
{
    class Program
    {
        static void Main(string[] args)
        {
            printFirstElementUppercase(null);
        }

        public static void printFirstElementUppercase(List<string> strList)
        {
            Console.WriteLine(strList[0]);
        }
    }
}

The below example shows how the previous example can be rewritten to prevent the null reference exception using the null-conditional element access operator (?[]). The element access operator (?[]) checks the operand to its left to check if it is null before accessing the element of the collection. If the operand to the left is null then null is returned. For the line Console.WriteLine(strList?[0]); the strList variable is to the left of ?[0], so the strList is checked if it has a value. It is null, so null is returned to prevent the null reference exception and a blank line is displayed to the console.

using System;
using System.Collections.Generic;

namespace NullConditionalExample
{
    class Program
    {
        static void Main(string[] args)
        {
            printFirstElementUppercase(null);
        }

        public static void printFirstElementUppercase(List<string> strList)
        {
            Console.WriteLine(strList?[0]);
        }
    }
}

Conclusion

The null-conditional operators (?. and ?[]) are used to the check if a variable is null before accessing it or invoking a method on it. Compared to using if conditions to check if variables have a value, the null-conditional operators provide shorter code that is easier to read and write.


References:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
https://www.youtube.com/watch?v=4iNAeYBe2GY
https://www.pluralsight.com/guides/using-conditional-and-null-coalescing-operators
https://csharp.today/c-6-features-null-conditional-and-and-null-coalescing-operators/

Leave a Comment

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