This topic is going to be simple, yet an interesting one. I’ll try to expose here my feelings about a certain case of null-checking.
It’s a well-known fact that introducing null-values considered by its inventor, Tony Hoare, as a billion-dollar mistake. Despite of some “modern” practices like making classes immutable, applying Null-Object pattern or using Code Contracts extensively, we still often want to declare an instance of an object as null (no instance). I’m not going to discuss the fundamental problem of null-values. Instead of that, I just want to consider a specific case concerning null-checking.
Several months ago I was reading some code in a ViewModel and stumbled upon a method implemented as it follows:
[code language=”csharp”]
bool CheckCard(CardDescription description, out CardReadingResult readingResult) {
readingResult = null;
if (description != null) {
CardReadingResult r = device.SearchForCard(description.CardType);
if (r == null)
Notify("Card was not inserted.");
else {
var info = CardInfoParser.GetInfo(r.CardInfo);
if (info.Number == description.Number) {
readingResult = r;
}
}
}
return readingResult != null;
}
[/code]
The method’s meaning payload is buried inside a bad method name and the uncertainty of the following check:
[code language=”csharp”]
…
if (description != null) {
…
}
[/code]
The overall meaning is that we have here some sort of checking the smart card. When readers look at this method, specifically at the line of the first null-checking, they will ask themselves about why we have one card description passed as an argument and after that we’re trying to search another card? The answer to this question, as it turned out, was that this method is performing a double-check. The first argument is the description of a smart card which has been already read on the previous step and we are trying here to check that at the current step we are handling the same smart card (that it wasn’t removed forcibly from the reading field, for example).
The main problem here is that there is no strong semantic cohesion between null-checking in the if-statement and the logic inside that if-statement. When I faced that code I questioned myself about what did actually mean the fact that the object is actually null or not. And the point is that this case is different from the vague logic inside the if-statement like in the following example:
[code language=”csharp”]
int zone = GetZone();
if (zone >= 2) {
DoAllowedStuff();
}
[/code]
There is a small, yet significant difference between these two if-statements. In the second example we have more information about the intrinsic logic of the method, despite of the uncertainty of what “2” actually means. We can guess the meaning easier than in the first case. Using NULL as a “value” adds to the information literally nothing.
It’s a good practice to extract checks like in the second example into a separate function:
[code language=”csharp”]
int zone = GetZone();
if (IsAllowedZone(zone) {
DoAllowedStuff();
}
bool IsAllowedZone(int zone) {
const int MinAllowedZone = 2
return zone >= MinAllowedZone;
}
[/code]
The same way we can act with the first example, or it might be better to apply Null-Object pattern.
What I wanted to say is that Null-checks could be completely uncertain and vague by the nature of the NULL as a state of an instance. NULL is nothing, but what does “nothing” actually means? No one knows.