In fact - the compiled msil is exactly the same but the example which
doesnt use "is" makes one extra read and write to the stack (tho not
being an expert in reading msil I couldnt tell you why)
try it
Actually that's not true at all. See
http://www.boyet.com/Articles/DoubleCastingAntiPattern.html for details. I
compiled the example code at that blog entry and got the following
results:
class Program
{
class Foo
{
public int Length { get { return 0; } }
}
static int DoubleCast(object obj)
{
if (obj is Foo)
return ((Foo)obj).Length;
return -1;
}
static int SingleCast(object obj)
{
Foo foo = obj as Foo;
if (foo != null)
return foo.Length;
return -1;
}
static void Main(string[] args) { }
}
DoubleCast compiles to this IL:
.method private hidebysig static int32 DoubleCast(object obj) cil managed
{
.maxstack 8
L_0000: ldarg.0 L_0001: isinst CastTest.Program/Foo
L_0006: brfalse.s L_0014
L_0008: ldarg.0 L_0009: castclass CastTest.Program/Foo
L_000e: callvirt instance int32 CastTest.Program/Foo::get_Length()
L_0013: ret L_0014: ldc.i4.m1 L_0015: ret }
and SingleCast compiles to this:
.method private hidebysig static int32 SingleCast(object obj) cil managed
{
.maxstack 1
.locals init (
[0] CastTest.Program/Foo foo)
L_0000: ldarg.0 L_0001: isinst CastTest.Program/Foo
L_0006: stloc.0 L_0007: ldloc.0 L_0008: brfalse.s L_0011
L_000a: ldloc.0 L_000b: callvirt instance int32
CastTest.Program/Foo::get_Length()
L_0010: ret L_0011: ldc.i4.m1 L_0012: ret }
The differeince is the additional "castclass" in DoubleCast at L_0012.
This actually results in two "castclass" operations if you consider the
definition of the "isinst" instruction that appears L_0002:
"isinst <token> (0x75). Check to see whether the object reference on the
stack is an instance of the class specified by <token>. <token> must be a
valid TypeDef, TypeRef, or TypeSpec token. This instruction takes the
object reference from the stack and pushes the result on the stack. If the
check succeeds, the result is an object reference, **as if castclass had
been invoked**; otherwise, the result is a null reference, as if ldnull
had been invoked." - Expert .NET 2.0 IL Assembler by Serge Lidin, pg. 287
(emphasis mine)
So, in the first example, the IL is not all that optimal because the
result of the "isinst" instruction is compared against null and thrown
away -- only to be recalculated later from the parameter by the
"castclass" instruction. In the second example, the result of the *isinst"
instruction is copied to a local so there isn't any need for "castclass".
Best Regards,
Dustin Campbell
Developer Express Inc.