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

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

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 здесь; как именно Jitter решает игнорировать инструкцию unbox_any в данном конкретном случае (теоретически, согласно msdn, исключение NullReferenceException должно генерироваться, когда инструкция isinst возвращает значение null, но на практике этого не происходит!)

Обновить

На основании ответа usr и комментария Ганса, если obj является ссылочным типом, будет вызываться 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 не выбрасывается?


person Dudi Keleti    schedule 20.12.2015    source источник
comment
public static void Test<T>(T o) where T : class Поскольку никогда не выбрасывалось исключение времени выполнения.   -  person Hamlet Hakobyan    schedule 20.12.2015
comment
@HamletHakobyan, ты прав на практике. но это должно быть в соответствии с msdn (см. ссылку msdn). причина, по которой исключение не было вызвано, заключается в том, что джиттер испускает инструкцию unbox_any (см. другую ссылку для ответа).   -  person Dudi Keleti    schedule 20.12.2015
comment
При применении к ссылочному типу инструкция unbox.any имеет тот же эффект, что и castclass typeTok. Если операнд typeTok является параметром универсального типа, то поведение во время выполнения определяется типом, указанным для этого параметра универсального типа. Где в документации указывается NRE?   -  person Yuval Itzchakov    schedule 20.12.2015
comment
@YuvalItzchakov Я видел это, но я хочу быть уверен, какова точная логика. так, проверка джиттера для типа и если это ссылочный тип, то unbox_any игнорируется?   -  person Dudi Keleti    schedule 20.12.2015
comment
@YuvalItzchakov NullReferenceException генерируется, если obj является нулевой ссылкой.   -  person Dudi Keleti    schedule 20.12.2015
comment
Просто посмотрите исходный код, если хотите доказательство. Обратите внимание на ярлык, который он принимает на !eeIsValueClass(resolvedToken.hClass)   -  person Hans Passant    schedule 20.12.2015
comment
@HansPassant Спасибо! Я пытался найти его сам, но безуспешно.   -  person Dudi Keleti    schedule 20.12.2015


Ответы (2)


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

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

В документации есть утверждение

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

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

person usr    schedule 20.12.2015
comment
в этом конкретном случае инструкция isinst возвращает значение null, и поэтому unbox_any должна вызывать NRE. NullReferenceException генерируется, если obj является пустой ссылкой. поэтому, если дрожание не игнорирует его, вы должны получить NRE, если будете запускать этот код. - person Dudi Keleti; 20.12.2015
comment
Я объяснил, почему это не так. Если не согласны объясните почему. Не просто заявляйте, что ответ неверен. Это мешает нам двигаться вперед. - person usr; 20.12.2015
comment
поэтому, если это тип значения, obj должен быть не нулевым, иначе будет выброшено NRE, и если это ссылочный тип, произойдет логика castclass. это имеет смысл, но если это правильно, почему должна происходить логика castclass, если мы просто делаем что-то подобное в isinst и уже получаем null? Я не говорю, что не согласен, я просто спрашиваю. - person Dudi Keleti; 20.12.2015
comment
Разговоры о типах val здесь спорны, потому что T ограничен типом ref. Если вы удалите ограничение where : class, вы больше не сможете использовать as (ошибка компилятора). - person usr; 20.12.2015
comment
Кажется, я нашел ответ в источнике. Мне еще предстоит понять это. Благодарю. - person Dudi Keleti; 20.12.2015
comment
Источник не спецификация. Это не лучший источник для объяснений. Вы уже знаете, что происходит, чтение источника не добавляет информации.; Документов MSDN здесь недостаточно. Спецификация ECMA CLI (ecma-international.org/publications /files/ECMA-ST/ECMA-335.pdf) говорит, что типы значений не упакованы. В I.8.2.4 B также говорится, что If the value type is a nullable type ... the result is a null reference or bitwise copy of its Value property. Я бы сказал, что это не совсем точно, но вы можете интерпретировать эти утверждения так, чтобы они означали то, что на самом деле происходит. - person usr; 20.12.2015
comment
Спасибо! Завтра проверю код по спецификации - person Dudi Keleti; 20.12.2015

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

Исключение System.NullReferenceException выдается, если obj имеет значение null, а typeTok является типом значения, не допускающим значение NULL.

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

Итак, резюмируя поведение unbox_any:

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

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

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

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

person Dudi Keleti    schedule 21.12.2015