I was wondering how is is operator implemented in C#.And I have written a simple test program (nothing special, just for demonstration purposes):
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();
}
}
}
And looked at the generated IL code:
.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
When I look at the documentation it says:
Pushes a null reference (type O) onto the evaluation stack.
for ldnull. Ofcourse, I wasn't expecting to see a source code here, but I'm surprised that there is only a null-check.I thought it may be relevant with compiler optimizations because Derived derives from Base so there is no check the compatibility about the types.then I check out and see that the optimizations are turned off.when I turn on the optimizations there wasn't even null-check.
So the question is why there is nothing generated about is operator ? why I see only a null-check ? Is it somehow relevant with is operator and I couldn't see ?
The type of d is Derived, which is always of type Base or null. That's why the non-optimized code only checks for null.
The optimized code doesn't do a check at all because the optimizer knows that d is not null (since you assigned a new object to it) and didn't change after the assignment.
d has compile-time type Derived, so if d is non-null, it is a Derived and a Derived is always a Base because of the inheritance.
You should not use is in a case like that; it is misleading.
The usual situation with is is the opposite one, where the compile-time type is Base and you check for is Derived.
As other have said, this is because the compiler already knows for sure what's happening there. If you wrap eveything in a method and use the is operator in the opposite direction you will see something more convincing:
static void f( Base c ) {
if ( c is Derived ) {
Console.WriteLine( "HELLO" );
}
}
Translates to:
.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
来源:https://stackoverflow.com/questions/24023705/when-i-use-is-operator-why-there-is-only-a-null-check-in-il-code