Eric Lippert, a member of the C# design team has an excellent little factoid about the C#
cast operator and how some code can lead to surprising results.
I’d like to take a step back and examine the problematic code from a different angle. First, let’s see the code:
object result; bool isDecimal = GetAmount(out result); decimal amount = (decimal)(isDecimal ? result : 0);
Aside from the fact that the decimal cast here will not work at run-time, do you, dear reader, see anything suspicious about this piece of code?
The problem is that the
GetAmount function uses an
out parameter. What this effectively does is allow
GetAmount to write back to our local
result variable. The
GetAmount function also returns a boolean value by the usual means, answering the question “is this amount a decimal”?
That first part of the code will work, but it makes the code less obvious and obstructs the purity of the code. That’s a somewhat subjective assessment but if all functions returned values using out then the code definitely would be much less readable.
So what to do here? We basically have the problem that we want to return two values: the amount and whether this amount is a decimal. Well, since C# 2.0, we’ve had nullable types. This means we can create a nullable type for any primitive (non-nullable) type. For example, a decimal is not nullable but we can create a variant that is:
Nullable<decimal> (the questionmark defines nullability). This means our decimal now has the potential to hold an extra value:
null. We can use this value to designate whether the value is a decimal or not.
So let’s first change the above code to take out “
decimal? temp = GetAmount(); decimal amount = temp.HasValue ? temp.Value : 0.0m;
That’s better and shorter. Can we make it shorter still? Allow me to introduce my new friend: the null-coalescing operator from C# 3.0:
decimal amount = GetAmount() ?? 0.0m;
The null-coalescing operator (
??) will evaluate to the right side of the operator (
0.0m in this case), if the left side of the operator happens to be
null; otherwise it will evaluate to the left side.
We can even save 4 extra characters by using C# 3.0 type-inference:
var amount = GetAmount() ?? 0.0m;
So what happened here is this:
- we took out
- we took out
- we took out two variables
- we took out two thirds of the code! (104 down to 33 characters)
In effect, we’ve seriously unspaghettified the code.
The reader might object: but what if we have 3 or more values we need to return. Well, in that case I have two answers:
- use objects, that’s what they’re there for, and
- use tuples, a new .NET Framework 4.0 type that allows easy return of multiple-values
This pattern here, of using nullable types is extremely useful and can help clean up code in many places. Not just for return values but also for parameters. They also have synergy with default parameters as introduced in C# 4.0.
I hope you’ll use this pattern instead and only use out parameters in very rare cases. In many cases, using
out parameters is an anti-pattern.