Introduction
There was a tweet from David Fowler the other day about the different ways in which you can do essentially the same thing in modern C# and it got me thinking.
Null checking in C# has gotten out of hand. I love the pattern matching and the ability to introduce variables as part of the check but there are too many ways to null check now. #dotnet #csharp pic.twitter.com/NA67zddx6y
— David Fowler 🇧🇧💉💉 (@davidfowl) August 1, 2021
Which do I like and why? Is one better than the other? Is it all subjective?
The Classic
Let's start off by looking at the classic null check which has been in C# since the beginning of the language.
var thing = Thing.Get(id);
if (thing != null)
{
thing.Do();
}
Pros: It's clear. It's familiar. It's been in the language since the beginning. Cons: It's verbose. People will argue it's not intuitive to people starting their developer journey. What is null?
Checking it's an Object
var thing = Thing.Get(id);
if (thing is object)
{
thing.Do();
}
Pros: Removes the magical null
check.
Cons: Not backwards compatible as is object
is relatively new concept. Need to understand what object
is now and how it relates.
Constant Pattern Matching
var thing = Thing.Get(id);
if (thing is not null)
{
thing.Do();
}
Pros: Clear English language being explicit what's going on.
Cons: Back to needing to know about null
but also combination of is
and not
. It's also not backwards compatibly as relatively new concept in the language. I can see this one catching on though.
Further reading - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#constant-pattern
Pattern Matching
var thing = Thing.Get(id);
if (thing is { } t0)
{
t0.Do();
}
Pros: Personally not sure there are any positives for this construct. Pattern matching to this syntax for this use case seems clunky.
Cons: High cognitive load to a mentally parse what is going on. Using pattern matching to check if it's an object doesn't feel right. Has an additional variable t0
to use.
Is a Thing
var thing = Thing.Get(id);
if (thing is Thing t1)
{
t1.Do();
}
Pros: I quite like this pattern. It doesn't really make sense in this scenario personally so might be classed as a con. I like this pattern match if there is inheritance going on and you want to check for a specific derived type.
Cons: Like I said before in this scenario it feels like overkill (although no benchmarks to back this up) just to check to see if thing
is null or not.
Further reading - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#declaration-and-type-patterns
Null Conditional
var thing = Thing.Get(id);
thing?.Do();
Pros: Clean with no nesting. Will only execute Do()
if thing
is not null.
Cons: Like a lot of the above it comes down to style, some developers do not like this syntax.
Further reading - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-
Conclusion
Like everything I think it comes down to "it depends". It depends on age of code base. It depends on internal coding standards. It depends on consistency of usage. It depends on target framework and language version being used. It depends on personal preference. There are a number of options above I do not like and some that I use nearly on a daily basis.
What are you thoughts? Reach out on twitter @WestDiscGolf and let me know!