What About .NET 1.1?
The nullable types described in this article were introduced to C# with version 2.0 of the .NET framework. In order to achieve similar results using earlier framework versions, the developer must create a new structure or class. Structures will be examined in a later article.
When a program works with numeric information, particularly when retrieved from a database, the value may be undefined. An example is when a series of simple yes / no questions is asked and the result of each question held in a Boolean. After a question is answered, the value can be either true or false to indicate the result. However, before the answer is given what should the value hold? The answer is null.
Nullable Numeric Data Types
Null is used to represent an undefined value. When C# was originally created, the value null existed but could not be applied to numeric variables. In C# 2.0, the basic data types have nullable equivalents. There are several ways to declare a variable as nullable. The simplest is to append a question mark (?) to the type. The following example shows the declaration and assignment of several nullable variables:
int? nullValue = null;
int? notNull = 123;
bool? answer1 = true;
bool? answer2 = false;
bool? answer3 = null;
You can see that creating a nullable variable is similar to creating a standard numeric variable. As with other numeric types, a value must be assigned before the variable is used, even if that value is null. The following code produces an error when you attempt to compile it.
int? copy = nullableInt; // Invalid as nullableInt is not yet assigned
Data Type Conversion
Numeric nullable data types allow implicit and explicit conversion between the various sizes of nullable integer and floating point types. Values can also be converted between their nullable and non-nullable versions. Conversion between two incompatibly sized types requires a cast statement, as does casting from a nullable to a non-nullable type.
int standardInteger = 123;
decimal standardDecimal = 12.34M;
// Implicit conversion from int to int?
nullableInteger = standardInteger;
// Explicit conversion from int? to int
standardInteger = (int)nullableInteger;
// Explicit cast from decimal to int?
nullableInteger = (int?)standardDecimal;
Care must be taken when casting a nullable value as a non-nullable data type. If the value is null a run-time exception will occur. This can be avoided by checking for null before attempting conversion.
The standard arithmetic operators can be used with nullable types. If the value of any of the operands is null, the result will be null regardless of the other values.
int? a = 55;
int? n = null;
result = a * 2; // result = 110
result = a * n; // result = null
When using nullable Booleans, the standard binary Boolean logical operators can be used. Where both of the operands used are either true or false, the results of the operation are the same as for non-nullable Booleans. Where one or both of the operands are set to null, the result is usually null. There are two special cases where this does not happen. In a logical OR operation, if any value is true the result is true, even if the other operand is null. For logical AND operations, if either value is false the result is false.
result = true & null; // result = null
result = false & null; // result = false
result = true | null; // result = true
result = false | null; // result = null
result = true ^ null; // result = null
result = false ^ null; // result = null
The relational operators are all valid for use with nullable numeric types. When the value being compared is null, the results are not always as expected. The equal to and not equal to operators are able to compare both numeric and null values. With all of the other relational operators, the result of the comparison is false when a value being compared is null.
int? a = 55;
int? n = null;
result = a == n; // result = false
result = a != n; // result = true
result = n == null; // result = true
result = a > n; // result = false
result = a < n; // result = false
1 October 2006