На этот вопрос уже есть ответ здесь:
Почему выводится этот фрагмент System.Int32
вместо Nullable
?
int? x = 5;
Console.WriteLine(x.GetType());
GetType()
является методом object
.
Чтобы вызвать его, структура Nullable<T>
должна быть в штучной упаковке.
Вы можете увидеть это в коде IL:
//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
Обнуляемые типы обрабатываются специально CLR; невозможно иметь упакованный экземпляр обнуляемого типа.
Вместо этого, упаковывая обнуляемый тип, вы получите пустую ссылку (если HasValue
false) или упакованное значение (если есть значение).
Следовательно, инструкция box System.Nullable<System.Int32>
приводит к выводу в штучной упаковке Int32
, а не в штучной упаковке Nullable<Int32>
.
Поэтому GetType()
невозможно когда-либо возвращать Nullable<T>
.
Чтобы увидеть это более четко, посмотрите на следующий код:
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());
}
Это печатает
Тип времени компиляции: System.Nullable`1 [System.Int32]
Тип времени выполнения: System.Int32
GetType()
не является виртуальным, и поэтому определяется только на object
. Таким образом, чтобы сделать звонок, Nullable<Int32>
должен быть сначала помещен в коробку. У Nullables есть особые правила бокса, поэтому в коробку помещается только значение Int32
, и это тип сообщения.
Потому что тип «5» является int.
Если вы хотите определить, является ли тип обнуляемым, и базовый тип, используйте что-то вроде этого:
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;
}