C# – forEach over branching over null check

cdesigndesign-patternsobject-orientedobject-oriented-design

I was watching Zoran Horvat's "Making Your C# Code More Object-oriented" on pluralsightpaywalled. And he says that instead of :

if(obj != null) { obj.DoSomething(); }

We should have a list that has either 1 or 0 no. of items of that type:

list.forEach(()=> obj.doSomething());

If the list has no objects it would not perform operation but if it does then it will have perform the operation. This will eliminate the need for branching over null.

But what I need your help with is, understanding the need to avoid branching over null here? Wouldn't a forEach be same as if here?

What the benefit?

Best Answer

Wouldn't a forEach be same as if here?

As far as branching goes yes. Loops have branches.

If you seriously need to avoid branching (maybe because of some branch prediction optimization issue) the branchless fix is the null object pattern where you create a class that has a DoSomething() method that does nothing, quietly.

Do that and the code becomes:

obj.DoSomething();

Now, when you need to do nothing, obj can be the quiet null object or the classic noisy null that throws a null pointer exception. Either way there was no branching. Just jumping to a set address that the branch predictor saw coming a long way off. This is the polymorphic object oriented solution.

So what does using

list.forEach(()=> obj.doSomething());

give you?

It gives you a way to create a "quiet null" without having to create a special class for it for every type that needs it. This takes the form of an empty collection. You've likely already used a version of this in the form of an empty string: "". Here they're doing the same thing with a list.

I wont say how well branch prediction handles that because engines get improved all the time. But seriously, don't worry about it unless you have to. Worry about readability first and those that come later can make it as fast as it needs to be because they can read it.

What all of this is ignoring is input validation. Unless you can trust your inputs you should check them. When null is a possible input, but not a valid input, you need to check it. This confuses the issue when your type system doesn't let you say that you don't accept noisy nulls.

In an ideal world excluding the noisy null could happen at compile time. Unfortunately when C#'s type system was first created they decided every reference type should be nullable.

But C# 8 has a new feature – non-nullable reference types. Now Foo isn't nullable. Only Foo? is. So in C#8 you don't need input validation for nulls. You can say you don't accept noisy nulls with the type system. The only reason you get noisy nulls is because you decided to accept them.

Fortunately these non-nullable reference types only exclude the noisy null. When you need to quietly do nothing the quiet nulls still work just fine.

Related Topic