Nullable Types Summary

Nullable types (supported from .NET 2.0) are instances of the System.Nullable<T> struct. A nullable type can represent the correct range of values for its underlying value type, plus an additional null value.

Nullable types are declared in one of two ways:

  • System.Nullable<T> variable

-or-

  • T? variable

The ability to assign null to numeric and Boolean types is especially useful when you are dealing with databases and other data types that contain 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. Yes, the nullable type modifier enables C# to create value-type variables that indicate an undefined value.

T is the underlying type of the nullable type. T can be any value type including struct; it cannot be a reference type. Reference types already support the null value.

Constructor

Although nullable type is value type, you can use constructor to initialize it. Note that using a no-argument constructor will give nullable variable a null, not the default value of its underlying type such as a zero.

// use the constructor with no argument
int? nint = new int?();
// it's equivalent to the following
int? nint = null;

// initialize nint to a specified value
int? nint = 0;

Properties:

Each instance of a nullable type has two public read-only properties: HasValue and Value.

Use the HasValue and Value read-only properties to test for null and retrieve the value, as shown in the following 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 HasValue is false. The Value property has no default value.
  • You can also use the == and != operators to do the same thing, as shown in the following example: if (x != null) y = x;

Conversions:

A variable of nullable type can be set to null with the null keyword, for example, int? n1 = null. The conversion from an ordinary type to a nullable type is also implicit, as int? n2 = 10.

If you want to cast a nullable type to a regular type, use either explicitly with a cast, or by using the Value property as shown above. Both conversions will create an exception if the nullable type variable is null.

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 produce a null value if the operands are null; otherwise, the operator uses the contained value to calculate the result.

When you perform comparisons with nullable types, if the value of one of the nullable types is null and the other is not, all comparisons evaluate to false except for != (not equal). It is important not to assume that because a particular comparison returns false, the opposite case returns true. For example, 10 is not greater than, less than, nor equal to null. Only 10 != null evaluates to true.

The ?? Operator

The ?? operator is called the null-coalescing operator and is used to define a default value for a nullable value types as well as reference types. It returns the left-hand operand if it is not null; otherwise it returns the right operand.

The ?? operator is very useful as it defines a default value that is returned when a nullable type is assigned to a non-nullable type.

Note that the ?? operator can also be used with multiple nullable types.

int? a = null;
// c = a, unless a is null, in which case c = -1.
int c = a ?? -1;
int? b = null;
// d = a or b, unless a and b are both null, in which case d = -1.
int d = a ?? b ?? -1;

The Bool? Type and Logical Operation

The bool? nullable type can contain three different values: truefalse and null.

When at least one operand is null, the results produced by the & and | operators are as follows:

X Y X & Y X | Y
true null null tru
false null false null
null null null null

Attention: the bool? type cannot be used in conditionals such as with iffor, or while. For example “if (null)” will cause a complier error.

The following example shows one way to safely cast from bool? to bool:

bool? test = null;
...// Other code that may or may not
// give a value to test.
if(!test.HasValue) //check for a value
{
// Assume that IsInitialized
// returns either true or false.
test = IsInitialized();
}
if((bool)test) //now this cast is safe
{
// Do something.
}

Methods:

GetValueOrDefault method retrieves either the assigned value, or the default value for the underlying type if the value is null. And the default value for the underlying type consists solely of binary zeroes.

GetValueOrDefault(T value) method retrieves the value of the current Nullable<T> object, or the specified default value. It functions just like the ?? operator.

Boxing and Unboxing

Objects based on nullable types are only boxed if the object is non-null. If HasValue is false, the object reference is assigned to null instead of boxing.

In other words, when a nullable type is boxed, the common language runtime automatically boxes the underlying value of the Nullable<T> object, not the Nullable<T> object itself. That is, if the HasValue property is true, the contents of the Value property is boxed. When the underlying value of a nullable type is unboxed, the common language runtime creates a new Nullable<T> structure initialized to the underlying value.

If the HasValue property of a nullable type is false, the result of a boxing operation is null. Consequently, if a boxed nullable type is passed to a method that expects an object argument, that method must be prepared to handle the case where the argument is null.When null is unboxed into a nullable type, the common language runtime creates a new Nullable<T> structure and initializes its HasValue property to false.

The behavior of nullable types when boxed makes the boxed objects identical to those created by boxing non-nullable types. Consequently, if a boxed nullable type variable is tested non-null, it then fully supports the functionality of the underlying type. (For example it can be unboxed to its underlying type.)

References:

[1] Nullable Types (C# Programming Guide)

[2] Using Nullable Types (C# Programming Guide)

[3] Nullable<T> Structure

Leave a Reply

Your email address will not be published. Required fields are marked *

:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: