Tuesday, April 12, 2011

The Nullable Structure

The Nullable Structure


One of the problems with most value types is the inability to specify that they contain an undefined value. This is unlike reference types, which may hold a null reference to indicate that they have no value. The Nullable structure resolves this issue.

Nullable Values

Most value types do not provide a means to indicate that they contain an undefined value. Unlike reference types, which are null by default and can be set to a null reference in code, uninitialised value types contain a default value that lies within their normal range. One way to work around the problem is to designate a particular value to indicate that a variable is undefined. For example, if you have a variable that should only contain positive integers, you may decide that -1 indicates that the user has yet to provide a value. This is problematic when all possible values could are valid. For example, you may wish to have a Boolean with three states: true, false and undefined. This is often the case when working will nullable information from databases.

The Nullable type is a structure that solves the problem of representing undefined values. It is a generic type that acts as a wrapper to any value type, adding the ability to store a null value. It also adds some useful methods and properties that make working with nullable value types easy. The type only permits wrapping of value types, as it would be meaningless to create a nullable version of a reference type, which is already nullable by definition. To prevent you from attempting this, the generic type parameter employs a value-type constraint.

NB: Nullable numeric types have been discussed as part of the C# Fundamentals Tutorial. Here they were seen using the ? syntax, where int? is equivalent to Nullable. It should be noted that any value type, including custom structures, can be made nullable using the Nullable structure.

Using Nullable

There are several ways in which a nullable type can be instantiated. The first way that we will examine is using a constructor. The Nullable structure defines two constructors. The default constructor has no parameters and creates a value that is initially set to null. The second constructor accepts a single argument of the type being wrapped. When used, this generates a nullable variable that contains the provided value.

In the sample code below, two nullable integers are instantiated. The first will contain the value 10 and the second will be null.

Nullable value = new Nullable(10);
Nullable nullValue = new Nullable();

The Nullable structure also permits values to be assigned directly to variables. The assigned value may be of the type expected or null. The process employs implicit casting from T to Nullable to create the new values.

Nullable value = 10;
Nullable nullValue = null;

HasValue and Value Properties


The Nullable structure provides various methods and properties that make working with nullable values easy. The first two that we will look at are the HasValue and Value properties. The HasValue property returns a Boolean value that is true if the type contains a defined value and false if it is null.

Try adding the sample code below after the two previous declarations to see the property in action:

bool hasValue;
hasValue = value.HasValue;      // true
hasValue = nullValue.HasValue;  // false

The Value property returns a non-nullable version of the value held in the nullable type. However, if the variable is set to a null reference, reading this property throws an InvalidOperationException. In many cases it is sensible to check the HasValue property before attempting to read the Value.

int nonNullable;
nonNullable = value.Value;      // 10
nonNullable = nullValue.Value;  // Exception

GetValueOrDefault Method

The GetValueOrDefault method provides a second means for reading the value from a nullable type. When used with no parameters, the method returns the wrapped value if one is present. If the value is null, the method returns the default value for the wrapped type. In the case of our wrapped integers, the default value is zero:

nonNullable = value.GetValueOrDefault();        // 10
nonNullable = nullValue.GetValueOrDefault();    // 0

If you do not wish to retrieve the type's default value when null, you can provide your own default value by passing it to the method as the only argument. The following code demonstrates this by returning 99 for null values.

nonNullable = value.GetValueOrDefault(99);      // 10
nonNullable = nullValue.GetValueOrDefault(99);  // 99

Casting


We have already seen that a non-nullable value can be implicitly cast to a nullable version of the same type. This was seen in the second code sample with the line:
Nullable value = 10;

There is no support for implicit casting of a nullable value to its non-nullable counterpart. However, the Nullable structure does implement explicit casting. The following assignment is therefore valid:
nonNullable = (int)value;

No comments:

Post a Comment