Когда я использую оператор, почему в 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
, и я не мог видеть?
Ответы
Ответ 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