Как упаковываются null-совместимые значимые типы?

Всем привет. В книге Рихтера написано:

При упаковке экземпляра Nullable<T> проверяется его равенство null и в случае положительного результата вместо упаковки возвращается null. В противном случае CLR упаковывает значение экземпляра. Следующий код демонстрирует такое поведение:

// После упаковки Nullable<T> возвращается null или упакованный тип T
Int32? n = null;
Object o = n; // o равно null

Да, конечно, o == null, но я думал, что там не должно быть упаковки, но она есть:

IL_0008: ldloc.0      // n
IL_0009: box          valuetype [System.Runtime]System.Nullable`1<int32>
IL_000e: stloc.1      // o
IL_000f: ret

Объясните кто-нибудь почему так происходит? Или это уже устаревшая информация?


Ответы (2 шт):

Автор решения: Dev18

Поведение зависит от значения переменной во время выполнения:

int? x = null;
object o = x;
Console.WriteLine(o == null); //тут выйдет true

IL показывает, что используется box для Nullable<int>, и это может ввести в заблуждение — как будто происходит упаковка.

Если HasValue == false (как в примере выше), то box в IL существует, но во время выполнения JIT просто присваивает null — никакого объекта не создаётся.

Если HasValue == true, то упаковывается значение базового типа int, а не сам Nullable

Таким образом, IL-код всегда содержит box, но реальное поведение зависит от значения и решается в рантайме

Это описано и в спецификации C#, и действительно соответствует тому, что говорит Рихтер.

Поле значения nullable_value_type создает ссылку null, если это значение NULL (HasValue равно false), или результат распакуивания и поля базового значения в противном случае.

→ Ссылка
Автор решения: Uranus

То, что в IL присутствует инструкция box Nullable<T>, не противоречит утверждению о том, что в CLR упаковка Nullable<T> ведёт себя особым образом: при HasValue = false результат - null, иначе - коробка со значением типа T. Это поведение реализуется на уровне JIT-компиляции, а не отражается напрямую в IL.

→ Ссылка