Monday, October 28, 2013

Null Coalescing Bugs in C#

Several times over the years, I've gotten questions like this:
 
I have a method which returns a nullable decimals telling me the sales figures for a given month. If the sales for a particular month have not been reported yet then I'd like to use zero in my calculations, instead of the null that is reported by the method. The null coalescing operator, ??, would seem to be designed to do exactly that, so I wrote the following code and it did not work as I expected; can you explain why?
 
total = GetSales(11) ?? 0.0m + GetSales(12) ?? 0.0m;
 
Or:
 
I have a user name in a string and nullable DateTime representing their birthday which I would like formatted as "username<birthday>" if there is a date value or "username<unknown>" if null. But this code doesn't work:
 
header = name + "<" + birthday ?? "unknown" + ">";
 
Before I continue: for readers unfamiliar with the null coalescing operator, it means "if the left side is not null, produce its value. If it is null, evaluate the right hand side and use it".  It's a handy little operator that was added to C# 2.0, but lots of people still do not know about it.
 
Both of these bugs have the same cause: the precedence of the null coalescing operator is lower than the precedence of the addition operator. Moreover, the second example has a deeper bug that is being hidden by the precedence bug; we'll come to that later.
 
What precisely do I mean by "precedence"? Operator precedence is essentially "automatic parenthesis insertion". When you say
 
a = b + c * d;
 
the compiler infers that you meant
 
a = (b + (c * d));
 
and not this, which would be semantically wrong:
 
a = ((b + c) * d);
 
or this, which would not even be a legal statement:
 
(a = b) + (c * d);
 
The rule is: when given a choice, the compiler inserts the parentheses around the subexpression which has the higher-precedence operator.

Read more: Coverity Community
QR: Inline image 1