Some C# obscurities
I’m sure everyone’s tired of hearing about C# 3.0 features like lambda expressions, extension methods, anonymous types and so on. Before you fall in love with the new features, there are a few oldies-but-goodies that revolve around the “?” character. I use a couple of these to stump interviewees who proclaim themselves to be C# experts. These question marks can provide a much cleaner, terser syntax for some fairly common C# usage patterns.
Conditional operator
This one can be easy to abuse, but it provides a nice terseness to code that has conditional assignments:
if (hoursTraveled > 0) speed = distanceInMiles / hoursTraveled; else speed = 0;
I’m trying to calculate speed, but clearly I don’t want to get DivideByZeroException. Sometimes these types of assignments can add up, so I like to condense them down with the C# conditional operator:
speed = hoursTraveled > 0 ? distanceInMiles / hoursTraveled : 0;
Now the conditional assignment can be written on a single line.
I don’t see this feature used very often, so there is a tradeoff in familiarity. If the conditional or assignment statements grow too large, it can start to hurt readability, so just use your best judgement on this one.
Nullable types
The release of the .NET Framework 2.0 brought along a little struct type that solved a whole heap of problems. Value types (structs) can be used to represent types that don’t care about referential identity. For example, if I have the number 2, and you have the number 2, they’re the same number no matter how many times we create it.
Value types have another interesting aspect, they can never have a null value. The details behind this are exciting if you like things Jeffrey Richter style, full of heap and stack knowledge, but in the end you just need to know that C# structs can never be null. This line does not compile:
int i = null;
But not every system in the world that deals with “int” recognizes this rule. Databases and XML schemas are two examples where “int” values can be null. To handle the impedance mismatch of real-world nulls and CLR-land value types, the Nullable
Nullable<int> i; i = null; Assert.That(i.HasValue, Is.False); i = 3; Assert.That(i.HasValue, Is.True); Assert.That(i.Value, Is.EqualTo(3)); Assert.That(i, Is.EqualTo(3));
Note that I have no problems assigning int values to the Nullable
int? i; i = null;
There’s our friend the question mark. It’s telling us “I think this variable is an int, but I’m not sure?”. This is just another compiler trick C# uses, just like extension methods. At compile time, “int?” is replaced with “Nullable
Before nullable types, I had to use a bunch of dirty tricks to represent nulls in my entities, usually with magic numbers and values like “Double.NaN” or “DateTime.MinValue”. Nullable types let me bridge the gap between the nullable and non-nullable worlds.
Null coalescing operator
This is the one I love to stump the self-proclaimed experts with. I draw this on the whiteboard:
??
And ask them, “what does this operator do in C#?” Usually I get the crickets, but the special few can tell me about the null coalescing operator. The null coalescing operator is very similar to the conditional operator, but with the conditional built-in. I find myself doing this quite a lot with nulls:
if (category.Description == null) output = "<Empty>"; else output = category.Description;
I have a value that could potentially be null, in this case the description of a category, but I need to output that value to a friendly format. Unfortunately, nulls aren’t always too friendly to end-users. Let’s try the conditional operator to see how that cleans things up:
output = category.Description != null ? category.Description : "<Empty>";
But these conditionals can get ugly, so I can use the “??” operator to provide an even terser syntax:
output = category.Description ?? "<Empty>";
All of these representations are equivalent, but I like the short and sweet syntax the “??” operator provides. Someone not familiar with this operator might not have any clue what the code does, so there is some level of risk involved.
But I generally don’t like to let a lack of knowledge with a built-in language feature deter me from using it, especially if it can provide a much cleaner syntax.
And as always…
If the syntax and usage these little question marks provide don’t provide better readability (solubility?), then don’t put them in. These features are there to help, not to satisfy technical fetishes. As always, keep in mind that your end goal is better readability and better maintainability, not a checklist of features used.