Джиттер логика для удаления unbox_any

Я расследую выполнение этого кода C#:

public static void Test<T>(object o) where T : class
{
    T t = o as T;
}

Эквивалентный код IL:

.method public static void  Test<class T>(object A_0) cil managed
{
  // Code size       13 (0xd)
  .maxstack  1
  .locals init (!!T V_0)
  IL_0000:  ldarg.0
  IL_0001:  isinst     !!T
  IL_0006:  unbox.any  !!T
  IL_000b:  stloc.0
  IL_000c:  ret
} // end of method DemoType::Test

Основываясь на этом ответе ( ненужный unbox_any), кто-нибудь может объяснить мне, что именно здесь делает джиттер; как именно Jitter решает проигнорировать инструкцию 'unbox_any' в этом конкретном случае (теоретически, согласно msdn, должно генерироваться исключение NullReferenceException, когда инструкция isinst возвращает ноль, но на практике этого не происходит!)

Обновить

На основании ответа usr и комментария Ганса, если объект является ссылочным типом, castclass будет называться, и, следовательно, нет NRE.

Но как насчет следующего случая?

static void Test<T>(object o) where T : new()
    {
        var nullable = o as int?;
        if (nullable != null)
            //do something
    }

Test<int?>(null);

И эквивалентный код IL (частичный):

IL_0001:  ldarg.0
IL_0002:  isinst     valuetype [mscorlib]System.Nullable`1<int32>
IL_0007:  unbox.any  valuetype [mscorlib]System.Nullable`1<int32>
IL_000c:  stloc.0
IL_000d:  ldloca.s   nullable
IL_000f:  call       instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
IL_0014:  stloc.1
IL_0015:  ldloc.1
IL_0016:  brfalse.s  IL_0024

В этом случае его значение типа, так почему NRE не выбрасывается?

2 ответа

Решение

При применении к ссылочному типу инструкция unbox.any имеет тот же эффект, что и castclass typeTok.

T ограничен, чтобы быть ссылочным типом. Эта инструкция не бросает NRE в этом случае. JIT не "игнорирует" его, он выполняет его так, как указано. JIT не может игнорировать инструкции.

В документации есть заявление

NullReferenceException генерируется, если obj является нулевой ссылкой.

что вводит в заблуждение, поскольку это относится только к типам значений. Первое утверждение, которое я привел, однозначно.

Чтобы ответить по второму случаю (раздел обновления по вопросу) о типе значений Nullable, нам нужно внимательно посмотреть в спецификации CLI ECMA (III.4.33 unbox.any - преобразовать коробочный тип в значение):

System.NullReferenceException генерируется, если obj имеет значение null, а typeTok является необнуляемым типом значения

Жирная часть отсутствует в документации MSDN.

Итак, подведем итог поведению unbox_any:

  1. Если typeTok является типом ref, то же поведение, что и для castclass
  2. Если typeTok является типом значения:

    2.1. Если obj имеет значение null, а typeTok является типом значения, допускающим значение NULL, результат будет равен NULL

    2.2. Если obj имеет значение null, а typeTok не имеет значения типа Nullable, будет выдано исключение NullReferenceException

Если я правильно понимаю, поведение пункта 2.2 такое же, как и при обычной распаковке, см. Jitter source code

Другие вопросы по тегам