Reputation: 101681
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 ?
Upvotes: 13
Views: 966
Reputation: 22979
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
Upvotes: 3
Reputation: 171774
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.
Upvotes: 17
Reputation: 61952
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
.
Upvotes: 8