Nullable notes
Nullable is value type object. But the following code can be compiled.
int? x = null
Isn't "null" supposed to be used with reference type, why it can be assigned with value type? It turns out to be just syntax suger, and the compiler emit the following code. It does not call any constructor.
IL_0001: ldloca.s x IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32>
However, if you write the following code, compiler will emit the msil like the following. It call the constructor.
int? y = 123; IL_0009: ldloca.s y IL_000b: ldc.i4.s 123 IL_000d: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0) //called public Nullable(T value) { this.value = value; this.hasValue = true; }
Nullable has two implicit converter that help you to write the following code.
int? y = 246; //implict conversion that create a Nullable on the fly, using the following implicit operator public static implicit operator Nullable<T>(T value) { return new Nullable<T>(value); } int z = (int)y; //explict conversion using the following explicit operator, this is not cast operation, this may throw exception, if Nullable.HasValue is false public static explicit operator T(Nullable<T> value) { return value.Value; } public T Value { get { if (!HasValue) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue); } return value; } }
You may wonder why we don't can write the following code?
int z = y; //error, you can not do this, because there is not implicit conversion
This is because we don't have implict converter to convert a Nullable<T> to T. If we have had the following operator, we will be able to write the code above.
public static implicit operator T(Nullable<T> value) { if (!HasValue) { return value.Value; } else { return default(T); } }
But Why we have explicit converter but not implicit converter. If we have had this operator, there will be no difference in using Nullable<T> and T. The purpose of Nullable<T> is to use value type T like a reference type. That is why in the Value property will throw exception if there is not value, we want to using a value type like a reference type!!! See the following example.
int? x = null; if (x == null)//msil will be like if (x.HasValue) { Console.WriteLine("x is null"); }
Although we can not implicitly convert Nullable<T> to T, but C# compiler and CLR, allow us use Nullable<T> like T in most of case. So the following code is legal.
int? y = 0; y++; //compiler will emit the following code //if (y.HasValue) //{ // int temp = y.Value; // temp++; // y = temp; //} Int32? x = 5; Console.WriteLine (x.GetType()); // it is "System.Int32"; not "System.Nullable<int32>" Int32? n = 5; Int32 result = ((IComparable) n).CompareTo(5); // Compiles & runs OK Console.WriteLine(result); // 0 /* If the CLR didn't provide this special support, it would be more cumbersome for you to write code to call an interface method on a nullable value type. You'd have to cast the unboxed value type first before casting to the interface to make the call: */ Int32 result = ((IComparable) (Int32) n).CompareTo(5); // Cumbersome
Null-Coalescing ?? operator works with reference type.
string s = null // string s2 = s ?? "something"; // this line is be compiled to IL_0003: ldloc.0 IL_0004: dup IL_0005: brtrue.s IL_000d IL_0007: pop IL_0008: ldstr "something" IL_000d: stloc.1
But c# compiler, make "??" works for Nullable as well. But underneath the emitted code is completely different, like the following
int z = y ?? 100; //it is equivalent as //z = (y.HasValue) ? y.Value : 100 //it is also equivalent as //z = y.GetValueOrDefault(100);
To sum this up, the purpose of Nullable type is to let a value type has a null value, but compiler also let us use it as value type as well.