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
.
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.
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
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.
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.
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.
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
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
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.