Given that this very natural use case (if you don't know what as actually does)
if (x is Bar) {
Bar y = x as Bar;
something();
}
is effectively equivalent (ie, compiler generated IL from the above code will be equivalent) to
Bar y = x as Bar;
if (y != null) {
y = x as Bar; //The conversion is done twice!
something();
}
EDIT:
I guess I hadn't made my question clear. I wouldn't ever write the second snippet as it's of course redundant. I'm claiming that the IL generated by the compiler when compiling first snippet is equivalent to the second snippet, which is redundant. Questions: a) Is this correct b) If so, why is is implemented like that?
This because I find the first snippet a lot clearer and prettier than the actually well written
Bar y = x as Bar;
if (y != null) {
something();
}
EDIT 2: Please try to avoid adding quotes to the title, if at all possible, thank you!
CONCLUSION:
Optimizing the is/as case is not the compiler's responsibility, but the JIT's.
Also, as with null check has less (and less expensive) instructions than both of the alternatives (is and as and is and cast).
Addendum:
IL for as with nullcheck (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldnull
L_000a: ceq
L_000c: stloc.1
L_000d: ldloc.1
L_000e: brtrue.s L_0019
L_0011: ldarg.0
L_0019: ret
IL for is and cast (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: castclass string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret
IL for is and as (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: isinst string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret
These have been edited for shortness (method declarations, nops and calls to something() removed)