Nullable Types in C#

The Value type objects are faster for the accessing and operations as compared to their reference counterpart. This makes them as a preferred choice for the data storage in the program. However there are certain limitations while using the value type objects in the application. This article gives a quick view in to the problems faced with the value type objects and the solution to the problems. This beautiful facility is introduced by Microsoft as an concept called “Nullable Types” in the C# 2.0.

In this article I will focus to answer two questions mainly those are:

  1. Why we need Nullable Types?
  2. What are the Nullable Types?

a. Why need Nullable type.

1. What will be the default value for the value type object?

Assigning a default value to a value type object is really a brainstorming process. Since this involves the use of the probability matrix, through which one can evaluate the possible values that a value type object can be assigned during its whole life span. Apart from finding the values that a object can have during its life span we also need to lay down a standard to consider some arbitrary value that lie outside the probability matrix; as the default value. This forces one rule on the project standards to consider the designated value as a default value and then force to follow the rule on the people working on the project.

Apart from this working and forced standards limitation; this default value occupies the space in the value matrix as default and unusable value limiting the future value for the object.

Let’s focus this with an example.

I have a structure about the student information I can write the structure as:

public struct student{

private int m_RollNumber; // Can range from 0 — 100

private string m_FullName ; // the non blank string name.

..

..

public student( int RollNumber, string FullName,…..){

//Initialize the members of the new object

this.m_RollNumber = RollNumber ;

this.m_FullName = FullName;

..

..

}

Even though I can provide the value for the roll number in the constructor, The C# still allows me to call the default constructor with no arguments. The default constructor will initialize the m_RollNumber to the zero and the m_FullName to the blank string.  Since m_RollNumber =0 is a valid value in the value matrix for the m_RollNumber. To denote the un-initialized m_RollNumber we need to assign a value to the m_Rollnumber that can be considered as default. Here since I assume that the value for the m_RollNumber must be between 0 – 100 through it’s life span. Based on this analysis I can consider values that are either Negative ( <0) or Positive (>100) as an default value.

Lets Say I decide the value 999 as my default for the roll number; I need to force this rule to check the value being 999 before any body use the m_RollNumber in the code, and also the rule to think the value as 999 as default and denoting the un-initialized m_RollNumber.

Thinking of the future impact when the roll number reaches the value 1000, the value 999 will be useless even though being a valid roll number.

2.  The Functions returning the Value type object cannot denote about the success of the execution:

Consider I have a collection of students where I search for any particular roll number.

public class Students

{

//The Internal array list maintaining the student list

private ArrayList m_Students = new ArrayList(100);

public Student GetStudentInfo(int RollNumber){

//Iterate through the list to find the student with roll number RollNumber

foreach( student stTemp in m_Students){

//Check if the stTemp is the required student.

If( stTemp.m_RollNumber == RollNumber){

//if yes return the stTemp.

Return stTemp ;

}

}

//If no match found return the empty object.

Return new Student();

}

}

The implementation of this function will return an object of the Student structure in all the cases even though the function fails to find the particular student[Since returning a blank object is done to suppress the compiler error.]. The caller of the function is still in ambiguity of the validity of the value returned. This is due to the fact that we can never assign the “null” value to the value type object.

To work around this problem we need to go for the ‘Out’ parameters. The modified version of the function is as follows:

public bool GetStudentInfo( int RollNumber, out Student outStudent){

//Initialize the outStudent object to a blank object.

outStudent = new Student();

//Iterate through the loop to find the student with roll number as RollNumber

foreach( student stTemp in m_Students)

{

If( stTemp.m_RollNumber == RollNumber)

{

//If the required object found then

//Save it to the out parameter and

outStudent = stTemp ;

//return true to indicate the success.

Return true;

}

}

//If object is not found then simply return the false to denote

the failure

Return false;

}

By calling this version of the function the caller has a way to find the validity of the returned object and in turn to check the success of the function execution through the returned Boolean value. However changing the function signature this way makes the function hard to understand and call.

To overcome the above mentioned problems with the default value and the return value from the function. We need some tactful way around this problem.

That’s the reason Microsoft has introduced the Nullable Types in the C# 2.0.

b. What are the Nullable types?

Nullable Types are the same generic data type with a wrapper structures written around the generic data type. This wrapper structures allow assigning the value “null” to the value type objects even.

If any object of the generic type has the ‘?’ attached to the data type while declaring the value object. It denotes the object is a Nullable object of that data type e.g. if I declare the RollNumber as

int? RollNumber = null ;

The RollNumber declared above is Nullable roll number that means assigning the value null to the Rollnumber is valid and completely acceptable. This reduces the burden to make some value as default and also since the function can return the Nullable types as a return type we have a way to return a null value through the object when the function fails.

An instance of the Nullable type has two public properties.

  • HasValue – A Boolean property denoting the validity of the value contained in the object.
  • Value – A value contained in the object.

The ‘true’ value for the HasValue property guarantees the validity of the value contained in the object which can be accessed through the Value property. A ‘false’ value for the HasValue indicates the invalid value contained by the object suggesting not to use the Value property.

An instance for which HasValue is false is said to be null. A null instance has an undefined value. Attempting to read the Value of a null instance causes a System.InvalidOperationException to be thrown.

Taking about the default values for the Nullable type we can say; Any instance of a Nullable type is said to have a default value when the HasValue is set to ‘false’ and the value property is undefined. This value is known as an null value of an Nullable type. While implicitly converting the ‘null’ value to any nullable type values the conversion results in the null value of the nullable type.

The following example shows the use of the Nullable type.

class Program

{

//the Nullable Roll number.

public int? RollNumber = null;

static void Main(string[] args)

{

Program pTemp = new Program();

//use the public member.

pTemp.RollNumber = 100;

//Write to console to check the object values.

Console.WriteLine(“Value in RollNumber is:{0} ,[HasValue]{1}”, pTemp.RollNumber.Value, pTemp.RollNumber.HasValue);

//test using the null.

pTemp.RollNumber = null;

Console.WriteLine(“Value in RollNumber is:{0} ,[HasValue]{1}”, pTemp.RollNumber.Value, pTemp.RollNumber.HasValue); //This line throws and InValidOperationException.

}

}

 

Nullable Types

Nullable types are instances of the Nullable struct. A nullable type can represent the correct range of values for its underlying value type, plus an additional null value. For example, a Nullable<Int32>, pronounced “Nullable of Int32,” can be assigned any value from -2147483648 to 2147483647, or it can be assigned the null value.

A Nullable<bool> can be assigned the values true, false, or null. 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 an example of when you might use a nullable type, consider how an ordinary Boolean variable can have two values: true and false. There is no value that signifies “undefined”. In many programming applications, most notably database interactions, variables can occur in an undefined state. For example, a field in a database may contain the values true or false, but it may also contain no value at all. Similarly, reference types can be set to null to indicate that they are not initialized.

This disparity can create extra programming work, with additional variables used to store state information, the use of special values, and so on. The nullable type modifier enables C# to create value-type variables that indicate an undefined value.

Nullable types can represent all the values of an underlying type, and an additional null value. Nullable types are declared in one of two ways:

System.Nullable<T> variable

-or-

T? variable

T is the underlying type of the nullable type. T can be any value type including struct; it cannot be a reference type.

Any value type may be used as the basis for a nullable type. For example:

int? i = 10;

double? d1 = 3.14;

bool? flag = null;

char? letter = ‘a’;

int?[] arr = new int?[10];

class NullableExample

{

static void Main()

{

int? num = null;

// Is the HasValue property true?

if (num.HasValue)

{

System.Console.WriteLine(“num = ” + num.Value);

}

else

{

System.Console.WriteLine(“num = Null”);

}

// y is set to zero

int y = num.GetValueOrDefault();

// num.Value throws an InvalidOperationException if num.HasValue is false

try

{

y = num.Value;

}

catch (System.InvalidOperationException e)

{

System.Console.WriteLine(e.Message);

}

}

}

The example will display the output:

num = Null

Nullable object must have a value.

The Members of Nullable Types


Each instance of a nullable type has two public read-only properties:

  • HasValue

HasValue is of type bool. It is set to true when the variable contains a non-null value.

  • Value

Value is of the same type as the underlying type. If HasValue is true, Value contains a meaningful value. If HasValue is false, accessing Value will throw a InvalidOperationException.

 

Nullable types have the following characteristics:

  • Ø  Nullable types represent value-type variables that can be assigned the value of null. You cannot create a nullable type based on a reference type. (Reference types already support the null value.)
  • Ø  The syntax T? is shorthand for Nullable, where T is a value type. The two forms are interchangeable.
  • Ø  Assign a value to a nullable type just as you would for an ordinary value type, for example int? x = 10; or double? d = 4.108. A nullable type can also be assigned the value null: int? x = null.
  • Ø  Use the Nullable.GetValueOrDefault method to return either the assigned value, or the default value for the underlying type if the value is null, for example int j = x.GetValueOrDefault();
  • Ø  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;
    • o   The HasValue property returns true if the variable contains a value, or false if it is null.
    • o   The Value property returns a value if one is assigned. Otherwise, a InvalidOperationException is thrown.
    • o   The default value for HasValue is false. The Value property has no default value.
    • o   You can also use the == and != operators with a nullable type, as shown in the following example: if (x != null) y = x;
  • Ø  Use the ?? operator to assign a default value that will be applied when a nullable type whose current value is null is assigned to a non-nullable type, for example int? x = null; int y = x ?? -1;
  • Ø  Nested nullable types are not allowed. The following line will not compile: Nullable<Nullable<int>> n;

Explicit Conversions


A nullable type can be cast to a regular type, either explicitly with a cast, or by using the Value property. For example:

int? n = null;

//int m1 = n;      // Will not compile.

int m2 = (int)n;   // Compiles, but will create an exception if n is null.

int m3 = n.Value;  // Compiles, but will create an exception if n is null.

If a user-defined conversion is defined between two data types, the same conversion can also be used with the nullable versions of these data types.

Implicit Conversions


A variable of nullable type can be set to null with the null keyword, as shown in the following example:

int? n1 = null;

The conversion from an ordinary type to a nullable type, is implicit.

int? n2;

n2 = 10;  // Implicit conversion.

 

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. For example:

int? a = 10;

int? b = null;

a++;         // Increment by 1, now a is 11.

a = a * 10;  // Multiply by 10, now a is 110.

a = a + b;   // Add b, now a is null.

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. In the following example, 10 is not greater than, less than, nor equal to null. Only num1 != num2 evaluates to true.

int? num1 = 10;

int? num2 = null;

if (num1 >= num2)

{

Console.WriteLine(“num1 is greater than or equal to num2”);

}

else

{

// This clause is selected, but num1 is not less than num2.

Console.WriteLine(“num1 >= num2 returned false (but num1 < num2 also is false)”);

}

if (num1 < num2)

{

Console.WriteLine(“num1 is less than num2”);

}

else

{

// The else clause is selected again, but num1 is not greater than

// or equal to num2.

Console.WriteLine(“num1 < num2 returned false (but num1 >= num2 also is false)”);

}

if (num1 != num2)

{

// This comparison is true, num1 and num2 are not equal.

Console.WriteLine(“Finally, num1 != num2 returns true!”);

}

// Change the value of num1, so that both num1 and num2 are null.

num1 = null;

if (num1 == num2)

{

// The equality comparison returns true when both operands are null.

Console.WriteLine(“num1 == num2 returns true when the value of each is null”);

}

/* Output:

* num1 >= num2 returned false (but num1 < num2 also is false)

* num1 < num2 returned false (but num1 >= num2 also is false)

* Finally, num1 != num2 returns true!

* num1 == num2 returns true when the value of each is null

*/

An equality comparison of two nullable types that are both null evaluates to true.

The ?? Operator

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

int? c = null;

// d = c, unless c is null, in which case d = -1.

int d = c ?? -1;

This operator can also be used with multiple nullable types. For example:

int? e = null;

int? f = null;

// g = e or f, unless e and f are both null, in which case g = -1.

int g = e ?? f ?? -1;

The bool? type


The bool? nullable type can contain three different values: true, false and null. For information about how to cast from a bool? to a bool,

Nullable Booleans are like the Boolean variable type that is used in SQL. To ensure that the results produced by the & and | operators are consistent with the three-valued Boolean type in SQL, the following predefined operators are provided:

bool? operator &(bool? x, bool? y)

bool? operator |(bool? x, bool? y)

The results of these operators are listed in the following table:

X

y

x&y

x|y

true true true true
true false false true
true null null true
false true false true
false false false false
false null false null
null true null true
null false false null
null null null null

 

Boxing Nullable Types


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. For example:

bool? b = null;

object o = b;

// Now o is null.

If the object is non-null — if HasValue is true — then boxing occurs, but only the underlying type that the nullable object is based on is boxed. Boxing a non-null nullable value type boxes the value type itself, not the System.Nullable<T> that wraps the value type. For example:

bool? b = false;

int? i = 44;

object bBoxed = b; // bBoxed contains a boxed bool.

object iBoxed = i; // iBoxed contains a boxed int.

The two boxed objects are identical to those created by boxing non-nullable types. And, just like non-nullable boxed types, they can be unboxed into nullable types, as in the following example:

bool? b2 = (bool?)bBoxed;

int? i2 = (int?)iBoxed;

The behavior of nullable types when boxed provides two advantages:

1.       Nullable objects and their boxed counterpart can be tested for null:

bool? b = null;

object boxedB = b;

if (b == null)

{

// True.

}

if (boxedB == null)

{

// Also true.

}

2.       Boxed nullable types fully support the functionality of the underlying type:

double? d = 44.4;

object iBoxed = d;

// Access IConvertible interface implemented by double.

IConvertible ic = (IConvertible)iBoxed;

int i = ic.ToInt32(null);

string str = ic.ToString();

 

 

How to: Identify a Nullable Type


Visual Studio 2010

  • ·Visual Studio 2008
  • ·Visual Studio 2005

You can use the C# typeof operator to create a Type object that represents a Nullable type:

System.Type type = typeof(int?);

You can also use the classes and methods of the System.Reflection namespace to generate Type objects that represent Nullable types. However, if you try to obtain type information from Nullable variables at runtime by using the GetType method or the is operator, the result is a Type object that represents the underlying type, not the Nullable type itself.

Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.

int? i = 5;

Type t = i.GetType();

Console.WriteLine(t.FullName); //”System.Int32″

The C# is operator also operates on a Nullable’s underlying type. Therefore you cannot use is to determine whether a variable is a Nullable type. The following example shows that the is operator treats a Nullable<int> variable as an int.

static void Main(string[] args)

{

int? i = 5;

if (i is int) // true

//…

}

Example


Use the following code to determine whether a Type object represents a Nullable type. Remember that this code always returns false if the Type object was returned from a call to GetType, as explained earlier in this topic.

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

 

How to: Safely Cast from bool? to bool

The bool? nullable type can contain three different values: true, false, and null. Therefore, the bool? type cannot be used in conditionals such as with if, for, or while. For example, the following code causes a compiler error.

bool? b = null;

if (b) // Error CS0266.

{

}

This is not allowed because it is unclear what null means in the context of a conditional. To use a bool? in a conditional statement, first check its HasValue property to ensure that its value is not null, and then cast it to bool. For more information, see bool. If you perform the cast on a bool? with a value of null, a InvalidOperationException will be thrown in the conditional test. The following example shows one way to safely cast from bool? to bool:

Example


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.

}

Written by 

Leave a Reply

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