For as long as I can remember, people have been asking why they can't set an
variable to null, or why they can't return null from a method declared to return
Many who understand why they couldn't do so still wished they could, particularly when working
.NET 2.0 provides the generic struct
System.Nullable<T> with the constraint that
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
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
is thrown if the value is null at that point. A nullable type can also be used as the right hand side of
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);
As well as the
System.Nullable<T> struct, there's the non-generic
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.
bool? has various binary logic operators, but not all of the ones available on
Importantly, the "shortcut" operators (
||) aren't defined for
bool?. A null value represents a sort of "don't know" value - so for instance,
null | true
null | false is
null & false
null & true is
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.)
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
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
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
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.
Back to the main C# page.