Введите 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. Компилятор С# позволяет этот код компилировать в любом случае.