CIL unbox_any инструкция - странное поведение

.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

Код C# равен:

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

Мои вопросы:

  1. Почему unbox.any звонили? если вы просто делаете

     var a = father as child 
    

    вызовет isinst intruction, а unbox.any не вызовет, и если я удалю обобщенное определение и попытаюсь привести (isinst) объект к какому-либо классу, unbox.any вызываться не будет.

  2. Может быть, unbox.any был вызван из-за общего определения, поэтому в этом случае unbox.any нужно вызвать исключение NullReferenceException, потому что ответ инструкции isinst возвращает null для этого приведения. смотрите unbox_any. И если вы попытаетесь запустить этот код, вы увидите, что не возникло никаких исключений.

Обновить

Я могу понять unbox_any, потому что параметр типа объекта, и он пытается привести его к конкретному типу после проверки isinst. Может быть, дженерики влияют также.

Мой вопрос: почему бы не выдать исключение в unbox.any, если объект, который мы пытаемся распаковать в T, равен нулю?

Документация гласит: "NullReferenceException выбрасывается, если obj является пустой ссылкой".

1 ответ

Решение

Распаковка должна держать проверяющего счастливым. Верификатор не особенно умел знать, что параметр типа T всегда будет ссылочным типом, и поэтому компилятор C# выдает эти ненужные в противном случае распаковки.

Если вы выполните поиск исходного кода Roslyn для Unbox_any и IsVerifierReference, вы увидите, что это происходит во многих местах вокруг генератора кода.

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

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