C# Coding Solutions—Nullable Types

Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio


Jump to: navigation, search
CSharp-Online.NET:Articles
C# Articles

C# Coding Solutions

© 2006 Christian Gross

Nullable Types: A Null Is Not Always a Null

In .NET null means no value, and we all understand what it represents, right? For example, the following code illustrates a null:

Object value = null;
 
if( value == null) {
    Console.WriteLine( "Yupe a null");
}

The example is obvious, and does nothing useful. If I attempted to call the method Object.ToString() an exception would be generated. So I have created is a variable value that references nothing and is nothing. This works because I am using a .NET reference type. Let’s write the same code, except we’ll use the value type int:

int value = 0;
 
if( value == 0) {
    Console.WriteLine( "Yupe a null?");
}

This time value is not of the type Object, but of the type int. And value is assigned a value of 0 to indicate a null value. In C++, and C, null and 0 have the same contextual meaning, so why not apply the same meaning to C#?

In .NET 2.0, and C# there is a new programming construct called nullable types. A nullable type in C# defines a variable that has a null state. For example, in the previous example the int value was assigned a value of 0, but a value of null would have been more appropriate. Yet we cannot assign a value null because the compiler will not let us. The compiler expects some valid value, and 0 is that valid value. The following example uses a struct to illustrate the restrictions of a value type even more clearly:

struct ExampleStructure {
    int value;
}
ExampleStructure ex = null;

The structure ExampleStructure is defined using the struct keyword, which means that ExampleStructure is a value type. The next line, in which the variable ex is assigned a value of null, will again result in a compilation error. It is not possible to assign a null value to a struct type because it is value type.

Assigning an int value to 0 resulted in code that compiled, but is logically incorrect. .NET considers a null a null, and a 0 as a valid value. .NET is not like C or C++ where null and 0 are interchangeable. This brings us back to square one and the inability to define a null for a value type. We would like to assign null values when situations occur where the algorithm has to indicate to the caller that no value for the parameter could be determined.

Nullable types make it possible for value types (such as structs) to behave like reference types and give value types the ability to indicate a null value. Nullable types are geared for usage by value types; they are not intended to be used in conjunction with reference types. There is no other reason to use a nullable type.

You don’t have to use nullable types; you could use the solution Java took. Java’s solution was to declare a reference type for each and every value type. It sounds good in theory, but in reality, it’s a real pain in the butt. In C# the notation to declare a value type as a nullable type for the ExampleStructure type is as follows:

ExampleStructure? ex = null;

You define a nullable type by attaching a question mark after the type declaration. As the preceding example illustrates, you can use nullable types to assign a null value to a value type. However, the C# compiler works some magic, illustrated in the generated IL of the example nullable type:

initobj valuetype [mscorlib]System.'Nullable`1'<
    valuetype ExampleStructure>

The C# compiler has converted the nullable type declaration into a type declaration that uses the .NET base class System.Nullable<>. The System.Nullable<> type is a .NET Generics class that has a single generic parameter that wraps the value type. It is not necessary to use the nullable type notation, and you could use the System.Nullable<> type directly, as follows:

System.Nullable< ExampleStructure> ex = null;

Compiling the example, which uses System.Nullable<>, generates the exact same IL as if you had used the C# nullable-type notation. The System.Nullable<> class allows you to perform operations on nullable values.

To understand nullable types, let’s go through an example that would perform a calculation using value types:

int unknown1 = 0;
int known1 = 10;
int result1 = unknown1 + known1;
 
ExampleStructure unknown2 = new ExampleStructure( 0);
ExampleStructure known2 = new ExampleStructure( 10);
int result2 = unknown2.value + known2.value;

In the example the int variable unknown1 represents an unknown value, and the ExampleStructure variable unknown2 represents an unknown struct value. The variables are unknown because in our pretend application, no value has been assigned to the variables. Yet technically, have been assigned in the source code values. Values have to be assigned because the compiler will complain about adding types without having them assigned.

So to satisfy the demands of the compiler, you assign the value types with zero values, but from an abstract logic perspective they are considered as null values. Now the compiler sees the variables assigned and compiles the sources. The problem is that the assignment was arbitrary and meaningless. If the variables were reference types, then doing a plain-vanilla assignment would have meant assigning the variables to null. Null is a valid reference value, and if a variable with a null value is manipulated, a null value exception error will result. Not so with the value types, as value types contain values and hence all manipulations, and in this case additions are syntactically correct.

This example illustrates how nullable types solve the problem of operating on an unknown value:

int? unknown1 = null;
int known1 = 10;
int result1 = (int)unknown1 + known1;
 
ExampleStructure? unknown2 = null;
ExampleStructure known2 = new ExampleStructure( 10);
int result2 = ((ExampleStructure)unknown2).value +
    known2.value;

In this modified example unknown1 and unknown2 are nullable types and assigned a value of null to indicate an unknown state. To be able to add the numbers, you need a typecast to convert the nullable type from a reference to a value. The code using nullable types functions identically to the code that uses only value types. However, with nullable types if the additions are attempted an exception (System.InvalidOperation) is generated. System.InvalidOperation indicates that the values are not consistent, and hence the operation cannot be carried out. This is the correct behavior, as we don’t want to add unknown values, and this clearly illustrates the need for nullable types.

Now let’s look deeper into the System.Nullable<> type. Following is the System.Nullable<> class declaration, abbreviated for illustration purposes:

namespace System {
    public struct Nullable<T> :
        System.IComparable, System.INullableValue  {
        public Nullable(T value) {}
 
        public static implicit operator
            System.Nullable<T > (T value) {}
        public static explicit operator T
            (System.Nullable<T > value) {}
        public static bool operator
            ==(System.Nullable<T > left,
            System.Nullable<T > right) {}
        public static bool operator
            !=(System.Nullable<T > left,
            System.Nullable<T > right) {}
 
        object INullableValue.Value {
             get {}
        }
        public virtual bool HasValue {
            get {}
        }
        public T Value {
            get {}
        }
    }
}

Notice that the declaration of System.Nullable<> is a structure, not a class. This is consistent with value type behavior because when you assign one value type with another value type, you are exchanging complete state and not references. The declaration of System.Nullable contains an implicit conversion to the System.Nullable<> type, but an explicit conversion to the .NET Generics’ type T. The implicit cast means you can assign one nullable type to another nullable type. The explicit cast means a typecast is needed to access the managed nullable type (such as int or ExampleStructure). The equals and does-not-equal operators are also implemented so that the managed type instance is compared rather than the nullable types. Finally, the properties INullableValue.Value, HasValue, and Value are read-only, making an instance of System.Nullable<> immutable.

The Nullable type is used in the following contexts:

  • When using value types to represent a null value state
  • When writing database applications to define record values that have null value states


Previous_Page_.gif Next_Page_.gif

Personal tools