I was wondering if anyone knows how the C# compiler handles the following assignment:
int? myInt = null;
My assumption is that there is an
I would expect .HasValue
is set to false
and .Value
is set to default(T), but I haven't checked that.
Something like:
public Nullable() {
this.value = default(T);
this.hasValue = false;
}
C# is a high-level language that get's compiled to IL.
With the introduction of nullable types, the C# standard changed, so the behavior of the C# compiler had to change to handle a new rule like "no struct, except Nullable, can be assigned a value of null".
Assigning null to a struct is generally disallowed, but that's just a rule the compiler enforces when generating the IL. Since the compiler parses all your code and figures out what it means, it can recognize all kinds of rules, even ones that, to you, may appear to be exceptions.
Basically, if the compiler parses your C# code and finds that you're assigning null to a struct, it outputs an error. If it finds that your assigning null to a Nullable<T>
struct, then it knows how to handle it and generates the appropriate IL.
From the C# standard:
13.7.1 Null type conversions: "An implicit conversion exists from the null type (§11.2.7) to any nullable type. This conversion produces the null value (§12.2) of the given nullable type."
12.2 Default values: "The default value of a nullable type is an instance for which the HasValue property is false. Referencing the Value property of a default value of a nullable type results in an exception of type System.InvalidOperationException. The default value is also known as the null value of the nullable type. An implicit conversion exists from the null type (§11.2.7) to any nullable type, and this conversion produces the null value of the type."
The statement:
int? myInt = null;
Gets compiled as:
.locals init (valuetype [mscorlib]System.Nullable`1<int32> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<int32>
Which, according to the MSDN, means «Initialize each field of the value type at a specified address to a null reference or a 0 of the appropriate primitive type.»
So there's no constructor or conversion here. HasValue will return false, and trying to get its Value will throw an InvalidOperationException. Unless you use GetValueOrDefault of course.
What really happens is that when you assign null
to the nullable type instance, the compiler simply creates a new instance of T?
, using the default constructor (the initobj IL instruction on Jb answer), so the following two lines are equivalent:
int? a = null;
Nullable<int> b = new Nullable<int>();
object.Equals(a,b); // true
Therefore you cannot do this:
Nullable<int> c = new Nullable<int>(null);
Something similar happens then you compare a nullable type to null:
if (a == null)
{
// ...
}
Behind the scenes it just does a call to the a.HasValue property.