当我使用is运算符时为什么在IL代码中只有空值检查?

时间:2014-06-03 19:48:37

标签: c# il

我想知道is operatorC#是如何实现的。我编写了一个简单的测试程序(没有什么特别的,仅用于演示目的):

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

当我看到documentation时,它说:

  

将空引用(类型O)推送到评估堆栈。

代表ldnull。当然,我不希望在这里看到源代码,但我很惊讶只有一个空检查。我认为它可能与编译器优化有关,因为Derived派生自Base所以没有检查类型的兼容性。然后我检查并看到优化是关闭。当我打开优化时,甚至没有 null-check

所以问题是为什么is运算符没有生成?为什么我只看到空检查?它与is operator有什么关系,我看不到?

3 个答案:

答案 0 :(得分:17)

d的类型为Derived,其类型始终为Base null 。这就是为什么非优化代码只检查null。

优化代码根本不进行检查,因为优化器知道d不为空(因为您为其分配了新对象)并且在分配后没有更改。

答案 1 :(得分:8)

d具有编译时类型Derived,因此,如果d为非空,则为DerivedDerived始终为{ {1}}因为继承。

在这种情况下你不应该使用Base;这是误导。

is的常见情况恰恰相反,编译时类型为is,您检查Base

答案 2 :(得分: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