Nullable<T> is a generic structure introduced with the .NET Framework 2.0 to support the concept of an undefined value. The T in Nullable<T> is a value type; reference types, like strings, support nullability by design. For instance, in C#:
String s = null; // works fine!
Int32 i = null; // compilation error: cannot convert null to 'int'
// because it is a non-nullable value type
Note that since Nullable<T> is a structure, it’s a value type itself and therefore fairly lightweight. It’s the same size as the underlying type with the additional overhead of a boolean field, and instances are still placed on the stack versus the heap.
Fundamental to the type are the two properties Value and HasValue. HasValue is a boolean property that indicates whether or not the Value property is assigned. The code snippet below, for instance, yields the output that follows and throws an InvalidOperationException on Line 10 because of the attempt to access the Value property of the null-valued j.
1: Nullable<Int32> i = 5;
2: Nullable<Int32> j = null;
5: Console.WriteLine(i == null);
9: Console.WriteLine(j == null);
As you might expect, there’s some shortcuts available too. For instance, you can access i.Value as just i, and i.HasValue == false is equivalent to the condition i == null. There’s also a GetValueOrDefault method available, which will perform the null test and return the default value of the underlying type if the current value is actually null.
i.HasValue == false
i == null
And there’s even a bit more syntactic sugar, as both C# and Visual Basic support the use of the ? as part of a synonym notation, so the following declarations are equivalent:
Visual Basic declarations
Dim i As Nullable(Of Int32)
Dim i As Int32?
Dim i? As Int32
Nullable types support both explicit and implicit conversions to their non-nullable counterparts, so consider the following C# snippet:
1: Int32 i;
2: Int32? j;
4: i = j.Value; // ok, but throws exception if j is null
5: i = (int) j; // ok, but throws exception if j is null
6: i = j; // compilation fails, explicit cast required
8: j = null; // null assignment
9: j = 10; // implicit cast to nullable type
10: j = i; // implicit cast to nullable type
Note, in Visual Basic, Option Strict is off by default, so in Visual Basic Line 6 would not cause a compilation error in the default scenario.
To make the scenarios in Lines 4 and 5 above bulletproof, you can test for the null in multiple ways as shown below:
2: Int32? j = null;
4: if (j.HasValue)
5: i = j.Value;
7: i = 0;
10: if (j != null)
11: i = (int)j;
13: i = 0;
16: i = j.GetValueOrDefault();
18: i = j ?? 0;
1: Dim i As Int32
2: Dim j As Int32? = Nothing
4: If (j.HasValue) Then
5: i = j.Value
7: i = 0
8: End If
10: If (j <> Nothing) Then
11: i = CType(j, Int32)
13: i = 0
14: End If
16: i = j.GetValueOrDefault()
18: i = If(j, 0)
Lines 4 through 16 are variations on constructs we’ve already discussed, and while they are all safe, the if constructs are clunky, and the GetValueOrDefault doesn’t offer much choice when the underlying value is indeed null. That’s where the coalesce operators on Line 16 (?? in C#, and If(x,y) in Visual Basic) come in. Each returns the value of the first argument (j) if the underlying value is non-null, and the second value (zero, here) if not.
This can come in handy when dealing with nullable boolean types for instance, since they cannot be used in C# conditional statements. Presuming you want to interpret a null as a false condition, the following snippet would work well; however, you might want to strongly consider whether use of three-valued boolean logic is a good idea in general.
Boolean? b = null;
if (b ?? false)
Console.WriteLine("Condition is true");
Console.WriteLine("Condition is false or null");
Visual Basic actually handles nullable booleans differently, and the following compiles just fine (even with Option Strict on) – bringing to the surface a subtlety in the implementation across the languages.
Dim b As Boolean? = Nothing
If (b) Then
Console.WriteLine("Condition is true")
Console.WriteLine("Condition is false or null")
For more in depth information on the treatment of null types, check out the following MSDN articles: