sept. 24

In .NET 2.0, the notion of nullable types has been added. But I have the feeling that lots of developpers miss some basic understanding about this. So I have decided to add here some thoughts and explanation about the nullable types.

Recall on nullable types

Note that a primitive type has the following declaration in the .NET framework:
    public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int>
In comparison, the Nullable<T> class has the following declaration:
    public struct Nullable<T> where T: struct

int? i = null;                    //Equivalent to "Nullable<int> i = null;" 
Console.WriteLine(i);             //Display "" 
Console.WriteLine(i.HasValue);    //Display "False" 
Console.WriteLine(i.Value);       //Generates a "InvalidOperationException : 
                                  //Nullable object must have a value" 
Console.WriteLine(typeof(int?));  //Display "System.Nullable`1[System.Int32]" 
Console.WriteLine(i.GetType());   //Generates a "NullReferenceException" 
 
int? j = 10;
Console.WriteLine(j);             //Display "10" 
Console.WriteLine(j.HasValue);    //Display "True" 
Console.WriteLine(j.Value);       //Display "10" 
Console.WriteLine(typeof(int?));  //Display "System.Nullable`1[System.Int32]" 
Console.WriteLine(j.GetType());   //Display "System.Int32" 

Recall on the default value (default operator)

On generic types, you have the “default” operator.
This operator returns the default value for a generic parameter type, meaning:

  • “null” for reference types
  • “Zero whitewash” for the value types meaning for the composing types:
    • Zero for all the number types
    • “null” for the reference types
    • “Zero whitewash” for the value types (non primitive).
public class GenericClass<T>
{
   public override string ToString()
   {
      object o = default(T);
      return o == null ? "null" : o.ToString();
   }
 
   public static void Main()
   {
      GenericClass<int> myInt = new GenericClass<int>();
      GenericClass<object> myObject = new GenericClass<object>();
      Console.WriteLine(myInt.ToString());    //Display “0” 
      Console.WriteLine(myObject.ToString()); //Display “null” 
   }
} 

The nullable types are in fact the structure “Nullable<T>”that expose a method “GetValueOrDefault”. This method will act as the “default” operator in case the nullable types do not have any value.

Basic arithmetic operations on nullable types

int? i = null;
int? j = 10;
 
//To be able to add the value of a nullable type, we can use the “Value” property 
//after testing that this type has a value. 
int total1 = 0;
if ( i.HasValue )
   total1 += i.Value;
if ( j.HasValue )
   total1 += j.Value;
Console.WriteLine(total1);
 
//Or we can use the “GetValueOrDefault” method on the object 
int total2 = i.GetValueOrDefault() + j.GetValueOrDefault();
Console.WriteLine(total2); 

It will of course depend of the cases, but usually the second syntax will allow having clearer (and shorter) code.

Comparison operators with nullable types

int? i = null;
int? j = 10;
int? k = 20;
 
Console.WriteLine(i < j);     //Display "False" 
Console.WriteLine(i > j);     //Display "False" 
Console.WriteLine(i == j);    //Display "False" 
Console.WriteLine(i != j);    //Display "True" 
 
Console.WriteLine(k < j);     //Display "False" 
Console.WriteLine(k > j);     //Display "True" 
Console.WriteLine(k == j);    //Display "False" 
Console.WriteLine(k != j);    //Display "True" 

So you can use the comparison operators without any problem. You don’t need to test the “HasValue” property before doing the comparison. Just keep in mind that this very short syntax is converted by the compiler as follows :

//The following syntax 
i < j 
//is transformed (at compile-time) into 
(i.GetValueOrDefault() < j.GetValueOrDefault()) && (i.HasValue & j.HasValue)

Just note that if you need to do a special treatment in the “null” case, you will have to use the “HasValue” property.

if ( i < j ) 
{ 
   // Only in the case i AND j have a value AND i.Value < j.Value 
} 
else 
{ 
   // Will be called whenever i OR j do not have a value OR i.Value >= j.Value 
} 
Tags:

Commentaires

Francois Wullens

Posted on mardi, 25 septembre 2007 19:56

Hi,
I'm tested a other case in Comparison operators with nullable types:

********************************************
    int? i = null;
            int? j = null;

            Console.WriteLine(i == j);//Display True
            Console.WriteLine();
            Console.WriteLine((i.GetValueOrDefault() == j.GetValueOrDefault()) && (i.HasValue & j.HasValue));//Display False
                      
            Console.ReadLine();
********************************************

Where is the Error?

Francois



Pierre-Emmanuel Dautreppe

Posted on mardi, 25 septembre 2007 20:23

Hello François !

In that case, your result is normal because:
"i == j" corresponds to "null == null" which is true.
"i.GetValueOrDefault() == j.GetValueOrDefault()" corresponds also to "null == null" because the default value of each nullable int is null.
But the last part "i.HasValue & j.HasValue" is false cause none of them have a value.

In fact the "translation" is done by the copiler for inequality comparison only. Why ? Simply because in that case the == operator will call the "Equals" method which does simply:
public override bool Equals(object other)
{
    if (!this.HasValue)
    {
        return (other == null);
    }
    if (other == null)
    {
        return false;
    }
    return this.value.Equals(other);
}

So no translation is needed.

Pierre-Emmanuel Dautreppe

Posted on mercredi, 26 septembre 2007 09:53

Correction of "typo" is the previous comment :

i.GetValueOrDefault() == j.GetValueOrDefault()" corresponds to "0 == 0" because the default value of each nullable int is "0".

Ajouter un commentaire




biuquote
  • Commentaire
  • Aperçu immédiat
Loading