Re: C# equivalent to TryCast

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



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.



.



Relevant Pages

  • Re: Exiting a calling word
    ... FOO) would be a visual indication? ... fail postpone rdrop postpone exit; ... The main difficulty here is not return stack manipulations, ... I also proposed the word TAIL ...
    (comp.lang.forth)
  • Re: Showing the methods class in expections traceback
    ... defined in class X or Y when one have the exact file and line? ... tracebacks to find which is the real problem. ... print "function bar.bar called on obj %s" % obj ... from foo import Foo ...
    (comp.lang.python)
  • Re: multi-threading in Forth?
    ... the use of data stack, discourages the use of return stack, ... For FOO defined as ... r[qux] stands for a code fragment that places the address of the code fragment qux onto the return stack. ... because exit does not pass control to the code that follows it. ...
    (comp.lang.forth)
  • Re: Forth tutorial like Ketmans
    ... explicit rather than implicit. ... foo(bar, baz); ... and it's plain from a glance at the source that the "foo" function is ... Second, what you quoted were stack comments which, though great to have, ...
    (comp.lang.forth)
  • Re: Showing the methods class in expections traceback
    ... defined in class X or Y when one have the exact file and line? ... define within a class is function, ... print "function bar.bar called on obj %s" % obj ... from foo import Foo ...
    (comp.lang.python)