The rise of the Null-Object pattern in C# 8

Hello and welcome,

As I probably mentioned in some of my older posts, I’m a big fan of code restrictions that can be imposed by the IDE. I think this is a reason why stylecop was/is so popular (I don’t know since I mostly use Rider nowadays and Resharper for Visual Studio).

Because of this, whenever I start a new project, I always turn on the feature to treat warnings as errors, just to get them out of the way from the start.

With the addition of the null reference in C# 8, I turn on this feature as well, so that if I need to use a null, it will be out of an actual thought out decision and not by default, this in combination with the “warnings as errors” option, doesn’t even let me compile without writing my code thoughtfully in regards to nulls.

What is a null reference?

The link mentioned above goes into a lot more detail about it, but suffice to say that we have two new symbols to use in conjunction with reference types.

The usage of ? at declaration

In the past, only the value types such as custom structs or the base types like int, bool, double, etc. benefited from having a default value and as such if we wanted a nullable variant, we needed to prefix the type definition with a ? which is some syntactic sugar sprinkled over the fact that when the code compiles we would get a Nullable<T>, where T was of type struct, which would also give us access to the HasValue and Value properties to check if the variable is null.

In the case of references, the is no such background magic, but the compiler is smart enough now to know that in a non-nullable scenario (either by turning it on, for a section of code, a file or the whole project), that the reference variable (or field or property) cannot receive a null value and as such, it will highlight a warning or throw an error. This also means that because this is not a form of syntactic sugar to express something different like Nullable<T>, that we won’t have access to the HasValue and Value properties.

Since there is no point in a property for the value because the value is the variable itself, for those that was something akin to what Nullable<T> for reference variables declared as nullable, we could use an extension method, might not be as pretty but it does the job. Here’s an example of such an extension:

public static class NullableObjectExtensions
{
    public static bool IsNull<T>(this T objectInstance) where T : new()
    {
        return objectInstance is null;
    }

    public static bool IsNotNull<T>(this T objectInstance) where T : new()
    {
        return !objectInstance.IsNull();
    }
}

I like to have both of them just so that I don’t need to look for the not operator, and it makes it easier to read.

The usage of !for when we know something isn’t null

The ! operator is called the null-forgiving operator. So if we’re in a situation in which we know that something shouldn’t be null but we’re still getting errors, then we could postfix our variable with the a ! and the compiler will just ignore that check.

The need for this mostly comes from a shortcoming of the compiler, in that if we do have a nullable variable that is being checked by the IsNotNull extension method, even if the variable is not null we will still get the warning/error, because the compiler doesn’t follow the check we have in a different method, so we have to tell the compiler “yes I know there’s a risk of a null reference but I did just check it”.

One thing to note is that the compiler is smart enough to recognize if we checked for a null reference if it’s in the same method.

In the following code snippet, I will show when we will use the null-forgiving operator, when not, and another type of trick using pattern matching and the null-conditional operator

class Program
{
    static void Main(string[] args)
    {
        MyClassA a = new MyClassA();

        if (a.MyClassB.IsNotNull()) // checking for nullability with the extension method
        {
            a.MyClassB.Prop.ToString(); // this gives off a warning/error even if we checked for nullability
            a.MyClassB!.Prop.ToString(); // this not gives off a warning/error because we used the null-forgiving operator
        }

        if (a.MyClassB != null)
        {
            a.MyClassB.Prop
                .ToString(); // this will not give off a warning/error because the compiler sees that we checked for nullability in this code block
        }

        if (!(a.MyClassB is null))
        {
            a.MyClassB.Prop
                .ToString(); // this won't throw an warning/error as well because the compiler knows that we're negating a pattern match for null
        }

        if (a.MyClassB is {}) // this is a somewhat better way to check if something not null, instead of pattern matching with null and then negating it
        {
            a.MyClassB.Prop.ToString(); // this also won't throw an warning/error
        }
    }
}

class MyClassA
{
    public MyClassB? MyClassB { get; set; }
}

class MyClassB
{
    public string Prop { get; set; } = string.Empty;
}

public static class NullableObjectExtensions
{
    public static bool IsNull<T>(this T objectInstance) where T : new()
    {
        return objectInstance is null;
    }

    public static bool IsNotNull<T>(this T objectInstance) where T : new()
    {
        return !objectInstance.IsNull();
    }
}

Now that we saw how we can use null reference, check for them with the help of the compiler, it’s time to return to the topic at hand, and that is to use an alternative to null checks by using the Null-Object pattern.

Moving to Null-Object pattern.

Likewise, I will not go into great detail as to what this pattern is because there are a lot of great resources about design patterns that explain them.

To use this, we need to create a static field that will give us a default object.

Here is an example of the modifications in place to use the Null object.

class MyClassA
{
    public MyClassB MyClassB { get; set; } = MyClassB.None;
}

class MyClassB
{
    public static MyClassB None { get; } = new MyClassB();
    public string Prop { get; set; } = string.Empty;
}

By creating a static property that can only be read and with a default instance, we can start writing our code free of null checks, better yet since it’s a static, if we want to check if the field has been filled in with a proper instance, we just need to compare it to the None property since they will all use the same reference. So if we can have the following.

MyClassA a = new MyClassA();
if (a.MyClassB == MyClassB.None)
{
  // Place code here.
}

Of course, there is a downside to this and that is since they are all using the same reference, if we do change something on the None instance, then the change will be accessible everywhere that reference is being used.

So a few solutions are as follows:

  • Write your classes with encapsulation and immutability in mind (easier said than done), that would encourage constructor parameters. Of course by using this approach, then we are limited when using POCO classes for deserialization like for example from JSON, this could still work if deserializers can use reflection for setting private fields.
  • Do a hackish approach that is kinda ugly from my point of view but at least it works, and it’s better if it’s ugly rater than crash in production (time will tell if there is a better approach)

This is my hackish solution for now

void Main()
{
	MyClassA a = new MyClassA();
	Console.WriteLine(a.MyClassB.Prop); // this will display the default text we inputted at creation time
	a.MyClassB.Prop = "456"; // this will throw an exception if we tried and changed it
}

class MyClassA
{
	public MyClassB MyClassB { get; set; } = MyClassB.None;
}

class MyClassB
{
	private string _propValue = string.Empty;

	public static MyClassB None { get; } = new MyClassB {
		Prop = "123" // example of default data for our null object
	};
	
	public string Prop
	{
		get { return _propValue; }
		set
		{
			if (this == None)
			{
				throw new InvalidOperationException($"Cannot set {nameof(Prop)} on instance of {nameof(None)}");
			}
			_propValue = value;
		}
	}
}

The obvious downside to this approach is that you need a check for each property that gets exposed to changes.

Conclusion

I hope you enjoyed this exploration through the land of null references, how to guard, and work with them.

I also hope that my hackish approach will find a better form, maybe one day when Rosylin will support AOP (we can all dream right? 😉).

See you next time, and happy coding.

Vlad V.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s