Calendar

<<  décembre 2008  >>
lumamejevesadi
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008

(septembre 24, 2007 16:12)

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 
} 

Billets liés

Commentaires

septembre 25. 2007 18: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



Francois Wullens

septembre 25. 2007 19: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

septembre 26. 2007 08: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".

Pierre-Emmanuel Dautreppe

Ajouter un commentaire


 

  Country flag





Live preview

décembre 2. 2008 15:11

Powered by BlogEngine.NET 1.2.0.0 | Theme by Pierre-Emmanuel Dautreppe