Wednesday, February 6, 2008

C# Nullable Types

C# Nullable Types


Nullable types are instances of the System.Nullable struct. A nullable type can represent the normal range of values for its underlying value type, plus an additional null value. For example, a Nullable, pronounced "Nullable of Int32," can be assigned any value from -2147483648 to 2147483647, or it can be assigned the null value. A Nullable can be assigned the values true or false, or null. The ability to assign null to numeric and Boolean types is particularly useful when dealing with databases and other data types containing elements that may not be assigned a value. For example, a Boolean field in a database can store the values true or false, or it may be undefined.

class NullableExample
{
static void Main()
{
int? num = null;
if (num.HasValue == true)
{
System.Console.WriteLine("num = " + num.Value);
}
else
{
System.Console.WriteLine("num = Null");
}

//y is set to zero
int y = num.GetValueOrDefault();

// num.Value throws an InvalidOperationException if num.HasValue is false
try
{
y = num.Value;
}
catch (System.InvalidOperationException e)
{
System.Console.WriteLine(e.Message);
}
}
}

The above will display the output:
num = Null
Nullable object must have a value.
Nullable Types Overview
Nullable types have the following characteristics:
· Nullable types represent value-type variables that can be assigned the value of null. You cannot create a nullable type based on a reference type. (Reference types already support the null value.)
· The syntax T? is shorthand for System.Nullable, where T is a value type. The two forms are interchangeable.
· Assign a value to a nullable type in the same way as for an ordinary value type, for example int? x = 10; or double? d = 4.108;
· Use the System.Nullable.GetValueOrDefault property to return either the assigned value, or the default value for the underlying type if the value is null, for example int j = x.GetValueOrDefault();

· Use the HasValue and Value read-only properties to test for null and retrieve the value, for example if(x.HasValue) j = x.Value;

· The HasValue property returns true if the variable contains a value, or false if it is null.

· The Value property returns a value if one is assigned, otherwise a System.InvalidOperationException is thrown.

· The default value for a nullable type variable sets HasValue to false. The Value is undefined.

· Use the ?? operator to assign a default value that will be applied when a nullable type whose current value is null is assigned to a non-nullable type, for example int? x = null; int y = x ?? -1;

· Nested nullable types are not allowed. The following line will not compile:


Using Nullable Types

Nullable types can represent all the values of an underlying type, and an additional null value. Nullable types are declared in one of two ways:
System.Nullable variable
-or-
T? variable
T is the underlying type of the nullable type. T can be any value type including struct; it cannot be a reference type.
This disparity can create extra programming work, with additional variables used to store state information, the use of special values, and so on. The nullable type modifier enables C# to create value-type variables that indicate an undefined value.

Examples of Nullable Types

int? i = 10;
double? d1 = 3.14;
bool? flag = null;
char? letter = 'a';
int?[] arr = new int?[10];

The Members of Nullable Types
Each instance of a nullable type has two public read-only properties:
· HasValue
HasValue is of type bool. It is set to true when the variable contains a non-null value.
· Value
Value is of the same type as the underlying type. If HasValue is true, Value contains a meaningful value. If HasValue is false, accessing Value will throw a InvalidOperationException.

Testing for a value can also be done like this:
if (y != null)

Explicit Conversions

A nullable type can be cast to a regular type, either explicitly with a cast, or by using the Value property. For example:

int? n = null;

// Will not compile.
int m1 = n;
// Compiles, but will create an exception if x is null.

int m2 = (int)n;
// Compiles, but will create an exception if x is null.
int m3 = n.Value;

Implicit Conversions

A variable of nullable type can be set to null with the null keyword, as shown below:
int? n1 = null;

The conversion from an ordinary type to a nullable type, is implicit.
int? n2;
n2 = 10; // Implicit conversion.

Operators

The predefined unary and binary operators, and any user-defined operators that exist for value types may also be used by nullable types. These operators produces a null value if the operands are null; otherwise, the operand uses the contained value to calculate the result. When performing comparisons with nullable types, if either of the nullable types is null, the comparison is always evaluated to be false.

The ?? Operator

A nullable type can contain a value, or it can be undefined. The ?? operator defines the default value to be returned when a nullable type is assigned to a non-nullable type. If you try to assign a nullable type to a non-nullable type without using the ?? operator, you will generate a compile-time error. If you use a cast, and the nullable type is currently undefined, an InvalidOperationException exception will be thrown.
The ?? operator defines a default value that is returned when a nullable type is assigned to a non-nullable type. The ?? operator returns the left-hand operand if it is not null, or else it returns the right operand.

int? c = null;

// d = c, unless c is null, in which case d = -1.
int d = c ?? -1;

This operator can also be used with multiple nullable types. For example:

int? e = null;
int? f = null;

// g = e or f, unless e and f are both null, in which case g = -1.
int g = e ?? f ?? -1;


The bool? Type

The bool? nullable type can contain three different values: true, false and null. As such, the cannot be used in conditionals such as with if, for, or while. For example, this code fails to compile with Compiler Error CS0266:

bool? b = null;
if (b) // Error CS0266.
{
}


Boxing Nullable Types

Objects based on nullable types are only boxed it the object is non-null. If HasValue is false, then, instead of boxing, the object reference is simply assigned to null. For example :

bool? b = null;
object o = b;
// Now o is null.

If the object is non-null -- if HasValue is true -- then boxing takes place, but only the underlying type that the nullable object is based upon is boxed. Boxing a non-null nullable value type boxes the value type itself, not the System.Nullable that wraps the value type. For example:

bool? b = false;
int? i = 44;
object bBoxed = b; // bBoxed contains a boxed bool.
object iBoxed = i; // iBoxed contains a boxed int.

The two boxed objects are identical to those created by boxing non-nullable types. And, like non-nullable boxed types, can be unboxed into nullable types, like this:

bool? b2 = (bool?)bBoxed;
int? i2 = (int?)iBoxed;