我想知道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
当我看到documentation时,它说:
将空引用(类型O)推送到评估堆栈。
代表ldnull
。当然,我不希望在这里看到源代码,但我很惊讶只有一个空检查。我认为它可能与编译器优化有关,因为Derived
派生自Base
所以没有检查类型的兼容性。然后我检查并看到优化是关闭。当我打开优化时,甚至没有 null-check
所以问题是为什么is
运算符没有生成?为什么我只看到空检查?它与is operator
有什么关系,我看不到?
答案 0 :(得分:17)
d
的类型为Derived
,其类型始终为Base
或 null 。这就是为什么非优化代码只检查null。
优化代码根本不进行检查,因为优化器知道d
不为空(因为您为其分配了新对象)并且在分配后没有更改。
答案 1 :(得分:8)
d
具有编译时类型Derived
,因此,如果d
为非空,则为Derived
且Derived
始终为{ {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