Джиттер логика для удаления 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:
- Если typeTok является типом ref, то же поведение, что и для castclass
Если typeTok является типом значения:
2.1. Если obj имеет значение null, а typeTok является типом значения, допускающим значение NULL, результат будет равен NULL
2.2. Если obj имеет значение null, а typeTok не имеет значения типа Nullable, будет выдано исключение NullReferenceException
Если я правильно понимаю, поведение пункта 2.2 такое же, как и при обычной распаковке, см. Jitter source code