Nullable types and the null coalescing operator

Nullable<T> and T?

For as long as I can remember, people have been asking why they can't set an int variable to null, or why they can't return null from a method declared to return DateTime. Many who understand why they couldn't do so still wished they could, particularly when working with databases.

.NET 2.0 provides the generic struct System.Nullable<T> with the constraint that T must be a value type. (If you know absolutely nothing about generics, now might be a good time to learn about the basics before reading further. You don't need to know a lot of the details however, and the basic concept is a lot simpler than full-blown generics sometimes gets, which is why I've put this page before the one on generics.) Nullable<T> itself is still a value type, but it represents the same set of values as T plus the "null" value. It maintains a separate member in memory, which is exposed through the HasValue property. When this is true, the Value property represents the overall value. When it's false, the overall value is null.

C# provides language support for nullable types using a question mark as a suffix. For example, int? is the same type as Nullable<int> (which is also the same type as Nullable<System.Int32> in the normal way). C# then allows you to compare a nullable value with null, or set it to null, and these work in the obvious way. There's an implicit conversion (no cast required) from a non-nullable type to its equivalent nullable type, and there's an explicit conversion (cast requried) from a nullable type to its equivalent non-nullable type. The cast is compiled into a call to the Value property, and an InvalidOperationException is thrown if the value is null at that point. A nullable type can also be used as the right hand side of the as operator, with the natural consequences.

The boxed type of a nullable value is the boxed type of the equivalent non-nullable value. If you box a value which is already null (i.e. HasValue is false), the result is null. This was a late change to the behaviour, as it required CLR changes which Microsoft were hoping to avoid - you may therefore see some beta documentation which disagrees with this.

Note that unlike in SQL, two null values of the same type are equal. In other words, the following:

int? x = null;
int? y = null;
Console.WriteLine (x==y);

prints "True".

As well as the System.Nullable<T> struct, there's the non-generic static class System.Nullable. This merely provides support for the System.Nullable<T> struct, in terms of finding out the non-nullable type of a nullable type and performing comparisons.

Nullable logic

bool? has various binary logic operators, but not all of the ones available on bool. Importantly, the "shortcut" operators (&& and ||) aren't defined for bool?. A null value represents a sort of "don't know" value - so for instance, null | true is true, but null | false is null; similarly null & false is false but null & true is null.

The null coalescing operator

This is a really simple little operator which I suspect will come in quite handy - if it's widely known about. (It was a long time before I saw anything about it.) Basically, a ?? b is similar to a==null ? b : a. The type of a has to be a nullable type or a reference type, and the type of b has to be a suitable type, the details of which are best left to the spec. The result is the value of a if that's non-null, otherwise it takes the value of b. a is only evaluated once (contrary to the version presented above using the conditional operator), and b is only evaluated at all if a evaluates to null.

The operator is right-associative, so a ?? b ?? c is equivalent to a ?? (b ?? c) - in other words, if you provide a string of a1 ?? a2 ?? ... ?? an then the result is the first non-null one (or null if all of them are null).

One nice feature is that if the type of a is a nullable type and the type of b is the equivalent "non-nullable" type, the result of the expression is that non-nullable type. For example, you can do:

// GetSomeValueMaybe() is a method returning an int? value
int? possible = GetSomeValueMaybe();
int definite = possible ?? 5; // Default to 5

// Alternatively, just:
int value = GetSomeValueMaybe() ?? 5;

This is possible because the compiler knows that if the first expression evaluates to null it will use the second expression. In this case, we're effectively using GetSomeValueMaybe() with a kind of "default value" of 5.

The usual details about conversions and the precise rules which are applied can be found in the spec.


Next page: Delegate changes
Previous page: Index and "bits and bobs"

Back to the main C# page.