Подтвердить что ты не робот

Когда я использую оператор, почему в IL-коде есть только нуль-проверка?

Мне было интересно, как is operator реализовано в C#. И я написал простую тестовую программу (ничего особенного, просто для демонстрационных целей):

class Base
{
    public void Display() {  Console.WriteLine("Base"); }
}

class Derived : Base { }

class Program
{
    static void Main(string[] args)
    {
        var d = new Derived();

        if (d is Base)
        {
            var b = (Base) d;
            d.Display();
        }
    }
}

И посмотрел на сгенерированный код IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       27 (0x1b)
  .maxstack  2
  .locals init ([0] class ConsoleApplication1.Derived d,
           [1] bool V_1,
           [2] class ConsoleApplication1.Base b)
  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApplication1.Derived::.ctor()
  IL_0006:  stloc.0  // set derived (d)
  IL_0007:  ldloc.0 // load derived
  IL_0008:  ldnull // push a null reference
  IL_0009:  ceq   // and compare with d !?
  IL_000b:  stloc.1
  IL_000c:  ldloc.1
  IL_000d:  brtrue.s   IL_001a
  IL_000f:  nop
  IL_0010:  ldloc.0
  IL_0011:  stloc.2
  IL_0012:  ldloc.0
  IL_0013:  callvirt   instance void ConsoleApplication1.Base::Display()
  IL_0018:  nop
  IL_0019:  nop
  IL_001a:  ret
} // end of method Program::Main

Когда я смотрю документацию, он говорит:

Отбрасывает нулевую ссылку (тип O) в стек оценки.

для ldnull. Конечно, я не ожидал увидеть исходный код здесь, но я удивлен, что есть только нуль-проверка. Я думал, что это может иметь отношение к оптимизации компилятора, потому что Derived происходит от Base, поэтому нет проверьте совместимость с типами. Затем я проверяю и вижу, что оптимизация отключена. Когда я включаю оптимизацию, не было даже нулевой проверки.

Итак, возникает вопрос, почему ничего не происходит в операторе is? почему я вижу только нуль-чек? Это как-то актуально с is operator, и я не мог видеть?

4b9b3361

Ответ 1

Тип d - Derived, который всегда имеет тип Base или null. Вот почему не оптимизированный код проверяет только null.

Оптимизированный код вообще не выполняет проверку, потому что оптимизатор знает, что d не является нулевым (поскольку вы назначили ему новый объект) и не изменился после назначения.

Ответ 2

d имеет тип времени компиляции Derived, поэтому, если d не является нулевым, это Derived, а a Derived всегда является Base из-за наследования.

Вы не должны использовать is в таком случае; это вводит в заблуждение.

Обычная ситуация с is является противоположной, где тип времени компиляции Base и вы проверяете is Derived.

Ответ 3

Как говорили другие, это связано с тем, что компилятор уже точно знает, что происходит там. Если вы обертываете eveything в методе и используете оператор is в противоположном направлении, вы увидите что-то более убедительное:

static void f( Base c ) {
    if ( c is Derived ) {
        Console.WriteLine( "HELLO" );
    }
}

Переведено на:

.method private hidebysig static void  f(class test.Base c) cil managed
{
  // Code size       31 (0x1f)
  .maxstack  2
  .locals init ([0] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  isinst     test.Derived
  IL_0007:  ldnull
  IL_0008:  cgt.un
  IL_000a:  ldc.i4.0
  IL_000b:  ceq
  IL_000d:  stloc.0
  IL_000e:  ldloc.0
  IL_000f:  brtrue.s   IL_001e
  IL_0011:  nop
  IL_0012:  ldstr      "HELLO"
  IL_0017:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001c:  nop
  IL_001d:  nop
  IL_001e:  ret
} // end of method Program::f