CSharp/C# Tutorial - C# Nullable Types






Reference types can represent a nonexistent value using a null reference.

Value types cannot ordinarily represent null values.

For example:

string s = null; // OK, Reference Type
int i = null;    // Compile Error, Value Type cannot be null

To represent null in a value type, you must use a special construct called a nullable type.

A nullable type is denoted with a value type followed by the ? symbol:

int? i = null; // OK, Nullable Type
Console.WriteLine (i == null); // True

Nullable<T> is an immutable structure, having only two fields, to represent Value and HasValue.





Implicit and explicit nullable conversions

The conversion from T to T? is implicit, and from T? to T is explicit.

For example:

int? x = 5; // implicit
int y = (int)x; // explicit

The explicit cast is directly equivalent to calling the nullable object's Value property.

Hence, an InvalidOperationException is thrown if HasValue is false.

Boxing and unboxing nullable values

When T? is boxed, the boxed value on the heap contains T, not T?.

This optimization is possible because a boxed value is a reference type that can already express null.

C# also permits the unboxing of nullable types with the as operator. The result will be null if the cast fails:

object o = "string";
int? x = o as int?;
Console.WriteLine (x.HasValue); // False




Operator Lifting

The Nullable<T> struct does not define operators such as <, >, or even ==. Despite this, the following code compiles and executes correctly:

int? x = 5;
int? y = 10;
bool b = x < y; // true

If both x and y have values, it compares via int's less-than operator; otherwise, it returns false.

Operator lifting means you can implicitly use T's operators on T?.

Here are some examples:


int? x = 1;
int? y = null;
//from   w  w w  . ja va  2 s  . co m
// Equality operator examples
Console.WriteLine (x == y); // False
Console.WriteLine (x == null); // False
Console.WriteLine (x == 1); // True
Console.WriteLine (y == null); // True
Console.WriteLine (y == 1); // False
Console.WriteLine (y != 1); // True


// Relational operator examples
Console.WriteLine (x < 6); // True
Console.WriteLine (y < 6); // False
Console.WriteLine (y > 6); // False


// All other operator examples
Console.WriteLine (x + 5); // 6
Console.WriteLine (x + y); // null

The compiler treats null logic differently depending on the category of operator.

Equality operators (== and !=)

This means two null values are equal:


Console.WriteLine ( null == null); // True
Console.WriteLine ((bool?)null == (bool?)null); // True

If exactly one operand is null, the operands are unequal.

If both operands are non-null, their Values are compared.

Relational operators

Comparing a null value to either a null or a non-null value returns false.

All other operators (+, -, *, /, %, &, |, ^, <<, >>, +, ++, --, !, ~) return null when any of the operands are null.

Mixing nullable and non-nullable operators

You can mix and match nullable and non-nullable types:


int? a = null;
int b = 1;
int? c = a + b; // c is null - equivalent to a + (int?)b

bool? with & and | Operators

When supplied operands of type bool? the & and | operators treat null as an unknown value. So, null | true is true.

Similarly, null & false is false.

The following example enumerates other combinations:


bool? n = null;
bool? f = false;
bool? t = true;
Console.WriteLine (n | n); // (null)
Console.WriteLine (n | f); // (null)
Console.WriteLine (n | t); // True
Console.WriteLine (n & n); // (null)
Console.WriteLine (n & f); // False
Console.WriteLine (n & t); // (null)
//  w  w  w .j a  va  2  s.c  om

Null Coalescing Operator

The ?? operator is the null coalescing operator, and it can be used with both nullable types and reference types.

If the operand is non-null, return it; otherwise, or return the default value.

For example:


int? x = null;
int y = x ?? 5; // y is 5

int? a = null, b = 1, c = 2;
Console.WriteLine (a ?? b ?? c); // 1 (first non-null value)

The ?? operator is equivalent to calling GetValueOrDefault with an explicit default value.