Nullable Types


C# 2.0 added a new feature that provides an elegant solution to what is both a common and irritating problem. The feature is the nullable type. The problem is how to recognize and handle fields that do not contain values (in other words, unassigned fields). To understand the problem, consider a simple customer database that keeps a record of the customer’s name, address, customer ID, invoice number, and current balance. In such a situation it is possible to create a customer entry in which one or more of those fields would be unassigned. For example, a customer may simply request a catalog. In this case, no invoice number would be needed and the field would be unused.

In the past, handling the possibility of unused fields required either the use of placeholder values, or an extra field that simply indicated whether a field was used. Of course, placeholder values can work only if there is a value that would otherwise not be valid, which won’t be the case in all situations. Adding an extra field to indicate if a field is in use works in all cases, but having to manually create and manage such a field is an annoyance. The nullable type solves both problems.

Nullable Basics

A nullable type is a special version of a value type that is represented by a structure. In addition to the values defined by the underlying type, a nullable type can also store the value null. Thus, a nullable type has the same range and characteristics as its underlying type. It simply adds the ability to indicate that a variable of that type does not contain a value. Nullable types are objects of System.Nullable<T>, where T must be a value type.

Remember 

Only value types have nullable equivalents.

A nullable type can be created two different ways. First, you can explicitly declare objects of type Nullable<T>, which is defined in the System namespace. For example, this creates int and bool nullable types:

 System.Nullable<int> count; System.Nullable<bool> done;

The second way to declare a nullable type is much shorter and is more commonly used. To declare a nullable type, simply follow the type name with a ?. For example, the following shows the more common way to declare a nullable int and bool type:

 int? count; bool? done;

A nullable object does not automatically contain null when it is created. Thus, you will often see a nullable object created like this:

 int? count = null;

This initializes count to null.

You can assign a value to a nullable variable in the normal way because a conversion from the underlying type to the nullable type is predefined. For example, this assigns count the value 100:

 count = 100;

There are two ways to determine if a variable of a nullable type is null or contains a value. First, you can test its value against null. For example, using count declared by the preceding statement, the following determines if it has a value:

 if(count != null) // has a value

If count is not null, then it contains a value.

The second way to determine if a nullable type contains a value is to use the HasValue read-only property defined by Nullable<T>. It is shown here:

 bool HasValue

HasValue will return true if the instance on which it is called contains a value. It will return false otherwise. Using the HasValue property, here is the second way to determine if the nullable object count has a value:

 if(count.HasValue) // has a value

Assuming that a nullable object contains a value, you can obtain its value by using the Value read-only property defined by Nullable<T>, which is shown here:

 T Value

It returns the value of the nullable instance on which it is called. If you try to obtain a value from a variable that is null, a System.InvalidOperationException will be thrown. It is also possible to obtain the value of a nullable instance by casting it into its underlying type.

The following program puts together the pieces and demonstrates the basic mechanism that handles a nullable type:

 // Demonstrate a nullable type. using System; class NullableDemo {   public static void Main() {     int? count = null;     if(count.HasValue)       Console.WriteLine("count has this value: " + count.Value);     else       Console.WriteLine("count has no value");     count = 100;     if(count.HasValue)       Console.WriteLine("count has this value: " + count.Value);     else       Console.WriteLine("count has no value");   } }

The output is shown here:

 count has no value count has this value: 100

Nullable Objects in Expressions

A nullable object can be used in expressions that are valid for its underlying type. Furthermore, it is possible to mix nullable objects and non-nullable objects within the same expression. This works because of the predefined conversion that exists from the underlying type to the nullable type. When non-nullable and nullable types are mixed in an operation, the outcome is a nullable value.

The following program illustrates the use of nullable types in expressions:

 // Use nullable objects in expressions. using System; class NullableDemo {   public static void Main() {     int? count = null;     int? result = null;     int incr = 10; // notice that incr is a non-nullable type     // result contains null, because count is null.     result = count + incr;     if(result.HasValue)       Console.WriteLine("result has this value: " + result.Value);     else       Console.WriteLine("result has no value");     // Now, count is given a value and result     // will contain a value.     count = 100;     result = count + incr;     if(result.HasValue)       Console.WriteLine("result has this value: " + result.Value);     else       Console.WriteLine("result has no value");   } }

The output is shown here:

 result has no value result has this value: 110

The ?? Operator

If you attempt to assign a nullable object to a variable of its underlying type, a System.InvalidOperationException will be thrown if the nullable object contains a null value. You can avoid this possibility by specifying a default value that will be assigned to the non-nullable variable when the nullable object contains null. To do so, use the ?? operator, which is called the null coalescing operator. It has this general form:

 nullable-object ?? default-value 

If nullable-object contains a value, then the value of the ?? operation is that value. Otherwise, the value of the ?? operation is default-value.

For example, in the following code balance is null. This causes currentBalance to be assigned the value 0.0 and no exception will be thrown.

 double? balance = null; double currentBalance; currentBalance = balance ?? 0.0;

In the next sequence, balance is given the value 123.75:

 double? balance = 123.75; double currentBalance; currentBalance = balance ?? 0.0;

Now, currentBalance will contain the value of balance, which is 123.75.

One other point: the right-hand expression of the ?? is evaluated only if the left-hand expression does not contain a value. The following program demonstrates this fact:

 // Using ?? using System; class NullableDemo2 {   // Return a zero balance.   static double getZeroBal() {     Console.WriteLine("In getZeroBal().");       return 0.0;   }   public static void Main() {     double? balance = 123.75;     double currentBalance;     // Here, getZeroBal( ) is not called because balance     // contains a value.     currentBalance = balance ?? getZeroBal();     Console.WriteLine(currentBalance);   } }

In this program, the method getZeroBal( ) is not called because balance contains a value. As explained, when the left-hand expression of ?? contains a value, the right-hand expression is not evaluated.

Nullable Objects and the Relational and Logical Operators

Nullable objects can be used in relational expressions in just the same way as their non-nullable counterparts. However, there is one additional rule that applies. When two nullable objects are compared using a relational operator, the result is false if either of the objects is null. For example, consider this sequence:

 byte? lower; byte? upper; lower = 16; upper = null; // Here, lower is defined, but upper isn't. if(lower > upper) // false

Here, the result of the > operator is false. However, somewhat counterintuitively, so is the inverse comparison:

 if(lower !< upper) // .. also false!

Thus, when one (or both) of the nullable objects used in a relational expression is null, the result of that comparison is always false.

When a logical expression involves two bool? objects, the outcome of that expression will be one of three values: true, false, or null (undefined). Here are the entries that are added to the truth table for the & and | operators that apply to bool?:

P

Q

P | Q

P & Q

true

null

true

null

false

null

null

false

null

true

true

null

null

false

null

false

One other point: When the ! operator is applied to a bool? value that is null, the outcome is null.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net