Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Nullable Types in C# - A Beginner's Tutorial

0.00/5 (No votes)
1 Sep 2012 1  
This article talks about Nullable types in C#. We will see when could we find ourselves in need for Nullable types and what should we know if we are dealing with Nullable types.

Introduction

This article talks about Nullable types in C#. We will see when could we find ourselves in need for Nullable types and what should we know if we are dealing with Nullable types.

Background

The null value is very useful in C#. For any type the null could be used to identify whether this particular variable contains a value or not. Typical use of null is to assign a variable null value to indicate that this variable has not been initialized. Whenever we need use this, we first check whether it is null or not, if it is null then perhaps we need to initialize and use it and if it is not then we can use it right away. 

Now if we consider the same scenario for built in numeric types or struct types, then How will we represent the absence of value. In an int type all the values are valid int values. The moment I declare int, it contains some default value. So is it possible to use null assignment and checking with int(or struct types)? 

The answer to the above question is - No, it is not possible to assign/check with null value for numeric types and struct types. So if we still need to have the possibility of checking for null for numeric types and structs, we need to define them as Nullable types.

Using the code 

So let us start by looking at how we can use null with reference types. Let us have a simple class and null will represent the absence of value. 

class A
{
    public void Display()
    {
        Console.WriteLine("Displaying from Class A.");
    }
}

static void Main(string[] args)
{    
    // For reference type variable, it is possible to check for null values
    A a = null;

    if (a == null)
    {
        a = new A();
        //use 'a' here
        a.Display();

        // with refeernce types it is also possible to assign null to them.
        a = null;
    }
}

How to create and use Nullable types

If this same entity would have need defined as an struct then it would not be possible to use null with it.

struct B
{
    public void Display()
    {
        Console.WriteLine("Displaying from Struct B.");
    }
}

static void Main(string[] args)
{  
    // For struct types it is not possible to have a null/assignment or check
    // Below line will generate a compile error.
    // B b = null;
}

So if we still need the object of class B to have the possibility of having null representing a null value then we need to create a Nullable type for B. A Nullable type is used to represent a possible missing value of a type. 

A Nullable type can be created as Nullable<T>. The T here is the type that cannot have null values. The type Nullable<T> will either have a null value or any possible value that T can have. So let us define a Nullable type for the above mentioned struct and see how we can use it.

static void Main(string[] args)
{ 
    // SO to use null with string we need to create a nullabe type if B

    Nullable<B> b2 = null;

    if (b2 == null)
    {
        b2 = new B();
        //use 'b' here
        b2.Value.Display();

        // Now with struct it is also possible to assign null since it is nullable
        b2 = null;
    }
}

And if we need to create a Nullable int then we can do it as

static void Main(string[] args)
{ 
    // Numeric types should also be defined nullable if we need to use null 
    Nullable$ltint> i = null;

    if (i == null)
    {
        i = 5;
        Console.WriteLine(i.ToString());
    }
}

Shorthand for using Nullable types

Instead of writing Nullable<T> every time, we can use the shorthand version for creating The Nullable types as T? t. Following code snippet shows how to use the shorthand notation to create the Nullable types.

static void Main(string[] args)
{
    // Short hand for using nullable types
    int? i2 = null;

    if (i2 == null)
    {
        i2 = 15;
        Console.WriteLine(i2.ToString());
    }
}

How is Nullable defined

The Nullable type is defined as a struct and this itself is a value type. Following code snippet shows the major properties and functions that we might be needing if we are using a Nullable type.

public struct Nullable<T> where T : struct
{
    public bool HasValue { get; }    
    public T Value { get; }   
    public T GetValueOrDefault();   
    public T GetValueOrDefault(T defaultValue);   
    public override string ToString();
}

The HasValue property will return true if the current Nullable<T> object has a value otherwise it will return false.

The Value property will return the value of the Nullable<T> object if the Nullable<T>.HasValue is true. if HasValue returns false then it will throw an exception.

The first version of GetValueOrDefault will return the value of the contained T type if it has some value otherwise it will return the T's default value. The second version will return the specified default value(passed as parameter) if the Nullable<T> has no value.

And finally the ToString method will return the string representation of the value contained in type T.

Conversion between Nullable and non-Nullable types

Once we create a Nullable type, we will find our self in need to convert it to and from the actual type. To understand how this conversion works, we need to look into how the conversion operators are defined in the Nullable type.

public struct Nullable<T> where T : struct
{
    public static explicit operator T(T? value);
    public static implicit operator T?(T value);
}

So looking at the above function declarations, it is clear that Conversion from T to Nullable is implicit but Conversion from Nullable to T is explicit.

static void Main(string[] args)
{
    // Converting To and From Nullable types
    int? i3 = null;
    int i4 = 50;

    // Conversion from T to Nullable is implicit
    i3 = i4;
    // Converstion from Nullable to T is explicit
    i4 = (int)i3;
}

Boxing and Unboxing Nullable types

If we perform boxing on a T? then the the T will be boxed and not the T?. This optimization is provided by the C# itself. Also we can unbox any value and get the Nullable type in return. This is useful when we need to check for null after unboxing. Following code snippet shows how the boxing and unboxing works in unison with Nullable types. 

static void Main(string[] args)
{
    // Boxing a nullable
    DateTime? dt = null;
    dt = DateTime.Now;

    // this will store DateTime and not DateTime?
    object o = dt;

    // Unboxing to a nullable
    DateTime? dt2 = o as DateTime?;

    if (dt2 != null)
    {
        Console.WriteLine(dt2.Value.ToString());
    }
}

Null Coalescing Operator

We have already discussed the GetValueOrDefault function above which will return the value of the contained T type if it has some value otherwise it will return the default value. This same thing can be achieved by using the Null coalescing operator ??. This operator is a syntactic sugar to perform the same operation as GetValueOrDefault(T defaultValue).

static void Main(string[] args)
{
    // Coalascing operator
    int? j = null;
    int? k = 54;

    int result1 = j ?? 0;
    int result2 = k ?? 0;

    // this will print 0 and 54
    Console.WriteLine("result1 = {0}, result2 = {1}", result1, result2);
}

Operators and Nullable types

Before we finish let us look at the use of operators with Nullable types i.e. Operator lifting.the concept of operator lifting is that if we use Nullable types with operators then it could use the operators on underlying types but once it has checked for null value. Though we can use on Nullable types as if we are using it for the containing types, there are some things to be kept in mind.

  • Unary operators: Result will be null if the Nullable types value is null.
  • Binary Operator: Result will be null if any of the operands is null.
  • Equality Operator: if both operands are null, result will be true. If only one of the operand is null, it is evaluated to false. otherwise underlying types will be compared for equality.
  • Relational Operator: if any operand is null, the result is false otherwise the comparison will be made on underlying type's values.

Point of Interest

In this small article, I have tried to talk about the Nullable types in C#. We have seen the basic concepts and usage principles of Nullable types.

History

  • 01 September 2012: First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here