tags:

views:

415

answers:

5

I was wondering if anyone knows how the C# compiler handles the following assignment:

int? myInt = null;

My assumption is that there is an implicit conversion performed, but I cannot figure out how the null literal assignment is handled. I dissasembled the System.Nullable object and found the implicit operator is overriden to this:

public static implicit operator T?(T value)  {
    return new T?(value);  
}

Once called this would try to fire the secondary constructor:

public Nullable(T value) {
    this.value = value;
    this.hasValue = true; 
}

Which is where my confusion comes into play... this.value is of some value type and cannot be null.

So, does anyone know how this "magic" takes place... or am I wrong in assuming that the secondary constructor is called? Does the default constructor get called because the compiler knows that it cannot match the second contructor's signature with the null literal (resulting in myInt being assigned to a new "null" Nullable)?

A: 

I would expect .HasValue is set to false and .Value is set to default(T), but I haven't checked that.

Joel Coehoorn
A: 

Something like:

public Nullable() {
    this.value = default(T);
    this.hasValue = false;
}
Justice
+7  A: 

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.

Jb Evain
Thanks Jb Evain, I should've thought to disassemble to IL.
Albert Oldfield
+1  A: 

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.

CMS
A: 

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."

Triynko