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;
}