Ask any programmer what ‘null’ means in C#, 99% of the time you will hear an answer that closely resembles this:
“Null – Amounting to nothing, non-existent, absent of value”.
Seems legit. But the really interesting thing about this definition is that it’s completely wrong. This is in fact, the English language definition of the word.
Now lets look at the actual C# definition of null:
“The null keyword is a literal that represents a null reference, one that does not refer to any object.”
And the Java definition of null:
“null is the reserved constant used in Java to represent a void reference i.e a pointer to nothing.”
What null means in C# & Java is simply that a pointer is invalid, eg. it does not reference a valid object in memory, nothing more. And yet almost every program I see abuses this simple concept by overloading the null keyword with additional semantics from the English language definition.
One very common example of this is in implementations of the repository pattern. The common convention is that when querying the repository, a return value of null indicates that no data was found for the given search criteria.
var item = myRepository.Get(id = 10);
if (item == null)
{
return "no item found with Id: 10";
}
// Do something with item...
It is often said that using Exceptions to control program flow is a bad idea. Given our new found understanding of the null keyword in C#, can we honestly say that using invalid pointers is any better?
What are the alternatives?
Lets think for a second about what we’re actually trying to say he when we return null from our repository. We’re trying to express the fact that the value may or may not be present. There is a data type in all programming languages that is perfectly suited to express this dichotomy – boolean.
In functional languages we can use Maybe/Option monads. In object oriented languages we can use the Null Object Pattern. However I think the simplest and most concise approach is to use the tester-doer pattern as described in Framework Design Guidelines (sometimes called the try/can pattern).
if (myRepository.RecordExists(id = 10))
{
var item = myRepository.Get(id = 10);
}
else
{
return "no item found with Id: 10";
}
This can be optimised to avoid two queries to the database:
var item;
if (myRepository.TryGetItem(id = 10, out item))
{
// Do something with item...
}
else
{
return "no item found with Id: 10";
}
The resulting code is far clearer, and the null keyword is no longer being abused.
Image may be NSFW.
Clik here to view.
Clik here to view.
