Which code segment below is faster?
MyClass classInstance = new MyClass();
IDoWork iDoWorkClass = classInstance as IDoWork;
if (iDoWorkClass != null)
// Do something so compiler doesn't think it's dead code
if (classInstance is IDoWork)
IDoWork iDoWorkClassViaCast = (IDoWork)classInstance;
// Do same thing as above so we compare apples to apples
If you answered ‘neither, they are approximately the same’ – you would be right based on my tests and analysis.
To understand why, look at the IL that’s produced (I added comments in italic to ease the reading of the IL code below):
IL for the first code snippet (using ‘as’):
// Code size 31 (0x1f)
// declare the maximal number of variables we plan to have on the stack at any given time
// declare variables and assign initial values to them
.locals init ( class AsIsComparison.MyClass classInstance,
 class AsIsComparison.IDoWork iDoWorkClass,
 bool CS$4$0000)
// NOP (no operation) instructions in the assembly code are needed to align labels to specific boundaries. This makes instruction-fetch operations more efficient for some processors
// Create new object of type AsIsComparison.MyClass
IL_0001: newobj instance void AsIsComparison.MyClass::.ctor()
// store the current stack value in local variable 0, i.e. classInstance (see declaration above)
// load variable 0 onto stack
// store the current stack value in local variable 1 -- iDoWorkClass
// load variable 1 onto stack
// Push a null reference onto the evaluation stack
// Compare two values. If they are equal, the integer value 1 (int32) is pushed onto the evaluation stack; otherwise 0 (int32) is pushed onto the evaluation stack.
// store the current stack value in local variable 2 -- (bool CS$4$0000, which is a compiler created variable)
// load variable 2 onto stack
// Transfers control to a target instruction (at address IL_001e) if value is true, not null, or non-zero.
IL_000f: brtrue.s IL_001e
// Fill space if opcodes are patched. No meaningful operation is performed although a processing cycle can be consumed.
// load the string constant onto the stack
IL_0012: ldstr "testing"
// Invoke method
IL_0017: call void [System]System.Diagnostics.Debug::WriteLine(string)
IL for the second code snippet (using ‘is’ + cast):
 class AsIsComparison.IDoWork iDoWorkClassViaCast,
IL_000d: brtrue.s IL_001e
// load variable 0 (classInstance) onto stack
// store the current stack value in local variable 1 (iDoWorkClassViaCast)
The code size is the same… The number of operations is the same… The operations themselves are the same (the order varies in a couple of places)… That’s why the performance is the same!