Введите int? против типа int
У меня есть это сравнение, которое равно false
как и ожидалось
bool eq = typeof(int?).Equals(typeof(int));
теперь у меня есть этот код
List<object> items = new List<object>() { (int?)123 };
int result = items.OfType<int>().FirstOrDefault();
но это возвращает 123
- в любом случае, это значение имеет тип int?
Как это может быть?
Ответы
Ответ 1
Обнуляемые типы имеют специальные правила "бокса"; "бокс" - это когда тип значения рассматривается как object
согласно вашему коду. В отличии от обычных значений-типов, обнуляемое значение типа упаковываются либо в качестве null
(обычный null
, нет типа), или как не-обнуляемого типа (The T
в T?
). Итак: int?
помечается как int
, а не как int?
, Затем, когда вы используете OfType<int>()
для него, вы получаете все значения, которые являются int
, а именно: одно переданное вами значение, поскольку оно имеет тип int
.
Ответ 2
Обнуляемый тип значения упакован по следующим правилам
- Если
HasValue
возвращает false
, HasValue
нулевая ссылка. - Если
HasValue
возвращает true
, значение базового значения типа T
HasValue
штучной упаковке, а не экземпляром nullable.
В вашем примере второе правило соблюдается, поскольку у вас есть значение:
var i = (object)(int?)123;
Ответ 3
Это немного поздно, но помимо ответа Марка на ваш вопрос, я хочу дать некоторую дополнительную информацию о типах значений Nullable в CLR.
CLR имеет встроенную поддержку типов значений, допускающих значение NULL. Эта специальная поддержка предоставляется для бокса, распаковки, вызова GetType
, вызова методов интерфейса.
Например, давайте проверим GetType()
:
Int32? x = 5;
Console.WriteLine(x.GetType());
Как вы думаете, это будет печатать на консоли? System.Nullable<Int32
? Нет, результат - System.Int32
.
Или позвольте проверить бокс, который вы отметили в своем вопросе:
Int32? n =5;
Object o = n;
Console.WriteLine("o type={0}", o.GetType()); // "System.Int32"
Правило таково:
Когда CLR упаковывает экземпляр Nullable, он проверяет, является ли он пустым, и если да, то CLR на самом деле ничего не упаковывает, и возвращается null. Если экземпляр nullable не равен NULL, CLR извлекает значение из экземпляра nullable и помещает его в поле. Другими словами, Nullable со значением 5 упакован в упакованный Int32 со значением 5.
И, наконец, я хочу объяснить, как CLR добавляет специальную поддержку для вызова методов интерфейса из Nullable Types. Давайте посмотрим на это:
Int32? n = 5;
Int32 result = ((IComparable) n).CompareTo(5); // Compiles & runs OK
Console.WriteLine(result); // 0
В предыдущем коде я приводил n, Nullable<Int32>
к IComparable<Int32>
, тип интерфейса. Однако тип Nullable<T>
не реализует интерфейс IComparable<Int32>
как Int32
. Компилятор С# позволяет этот код компилировать в любом случае.