I\'m curious to know how the Nullable type works behind the scenes. Is it creating a new object(objects can be assigned null) with a possible value of null?
In the
The nullable type is a struct consisting of two fields: a bool
and a T
. When the value is null, the bool is false and the T has the default value. When the value is not null, the bool is true.
There are two main benefits to using Nullable
as compared to implementing the functionality yourself. There's the language support, as described in more detail in ChaosPandion's answer, and there's the fact that boxing (converting to an object
) will automatically remove the nullable "wrapper", leaving either a null reference or the plain T object.z
It's actually quite simple. The compiler gives you a hand with the syntax.
// this
int? x = null;
// Transformed to this
int? x = new Nullable<int>()
// this
if (x == null) return;
// Transformed to this
if (!x.HasValue) return;
// this
if (x == 2) return;
// Transformed to this
if (x.GetValueOrDefault() == 2 && x.HasValue) return;
Here is the (tidied up) code from running .Net Reflector against Nullable...
[Serializable, StructLayout(LayoutKind.Sequential), TypeDependency("System.Collections.Generic.NullableComparer`1"), TypeDependency("System.Collections.Generic.NullableEqualityComparer`1")]
public struct Nullable<T> where T: struct
{
private bool hasValue;
internal T value;
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
public bool HasValue
{
get
{
return this.hasValue;
}
}
public T Value
{
get
{
if (!this.HasValue)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
}
return this.value;
}
}
public T GetValueOrDefault()
{
return this.value;
}
public T GetValueOrDefault(T defaultValue)
{
if (!this.HasValue)
{
return defaultValue;
}
return this.value;
}
public override bool Equals(object other)
{
if (!this.HasValue)
{
return (other == null);
}
if (other == null)
{
return false;
}
return this.value.Equals(other);
}
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
public override string ToString()
{
if (!this.HasValue)
{
return "";
}
return this.value.ToString();
}
public static implicit operator Nullable<T>(T value)
{
return new Nullable<T>(value);
}
public static explicit operator T(Nullable<T> value)
{
return value.Value;
}
}
Nullable<T>
is implemented as a struct that overrides Equals()
to behave as null
if HasValue
is false
. There is an implicit conversion from T
to T?
, and an explicit conversion in the other direction which throws if !HasValue
.