Why GetType returns System.Int32 instead of Nullable<Int32>? [duplicate]

Why is the output of this snippet System.Int32 instead of Nullable<Int32>?

int? x = 5;
Console.WriteLine(x.GetType());

GetType() is a method of object.
To call it, the Nullable<T> struct must be boxed.

You can see this in the IL code:

//int? x = 5;
IL_0000:  ldloca.s    00 
IL_0002:  ldc.i4.5    
IL_0003:  call        System.Nullable<System.Int32>..ctor

//Console.WriteLine(x.GetType());
IL_0008:  ldloc.0     
IL_0009:  box         System.Nullable<System.Int32>
IL_000E:  callvirt    System.Object.GetType
IL_0013:  call        System.Console.WriteLine

Nullable types are treated specially by CLR; it is impossible to have a boxed instance of a nullable type.
Instead, boxing a nullable type will result in a null reference (if HasValue is false), or the boxed value (if there is a value).

Therefore, the box System.Nullable<System.Int32> instruction results in a boxed Int32, not a boxed Nullable<Int32>.

Therefore, it is impossible for GetType() to ever return Nullable<T>.

To see this more clearly, look at the following code:

static void Main()
{
    int? x = 5;
    PrintType(x);   
}
static void PrintType<T>(T val) {
    Console.WriteLine("Compile-time type: " + typeof(T));
    Console.WriteLine("Run-time type: " + val.GetType());
}

This prints

Compile-time type: System.Nullable`1[System.Int32]
Run-time type: System.Int32


GetType() isn't virtual, and is thus defined only on object. As such, to make the call, the Nullable<Int32> must first be boxed. Nullables have special boxing rules, though, so only the Int32 value is boxed, and that's the type reported.


You can't box a nullable.

You could do something like this:

public static Type GetCompilerType<T>(this T @object)
{
  return typeof (T);
}

int? x = 5;
Console.WriteLine(x.GetCompilerType());
// prints:
// System.Nullable`1[System.Int32]

Because the type of "5" is int.

If you want to detect if a type is nullable, and the underlying type, use something like this:

public static Type GetActualType(Type type, out bool isNullable)
{
    Type ult = Nullable.GetUnderlyingType(type);
    if (ult != null)
    {
        isNullable = true;
        return ult;
     }
     isNullable = false;
     return type;
}