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