Exploring Intermediate Language (IL) with ReSharper and dotPeek
When compiling .NET languages, our source code is not translated into native code for the platform it will run on. Instead, code is translated into Intermediate Language (IL) which can then be executed on a variety of platforms and CPU types. On the target machine, the .NET runtime will translate this IL code to native code and execute it. Both ReSharper and dotPeek allow us to explore our source code and the (decompiled) IL instructions. We can use it to learn how the compiler translates our code into IL.
The IL viewer can be opened from a few places, like the ReSharper | Windows | IL Viewer menu or the context menu in the editor (Navigate | IL Code). In dotPeek, it can be opened from the Windows | IL Viewer menu. The tool window will display the decompiled IL for the class or method we currently have open in the editor. If no IL is displayed, make sure to compile the project first as the IL is read from the compiled assembly.
When placing the caret on a symbol in the editor, the IL view is synchronized and matching code is highlighted. Synchronization also works in the other direction: when placing the caret on a block of IL code, the editor will jump to the corresponding code. This behavior can be toggled using the Track Caret in Editor and Automatically Scroll to Source toolbar buttons. If we place the caret on a setter of an auto-property, we can see the IL view jump to that location. We can see the compiler magic in action: the IL code clearly shows the C# compiler generated a backing field for our property.
The IL viewer helps us in understanding the IL code shown. There are comments in it, showing line and column numbers. For example, the
set keyword in the editor is on line 10, columns 37-41. Types, members, type parameters, local variables, etc. are highlighted with distinct colors, and loops in our code are indented and decorated with comments:
But what do all these IL instructions mean? The IL viewer shows the description, taken from MSDN and the ECMA-335 standard (Partition III: CIL Instruction Set), when hovering the mouse over an instruction.
When using the IL viewer to optimize code, it’s always best to compile with the Release configuration. In Debug, the compiler optimizes our code debug-ability, while in Release the focus is on speed. Most of the optimization is done by the .NET runtime when the application is run, but there are differences in the IL as well.
For example, in Debug we can see there are a number of
nop instructions in each method while in a Release build there are not. The
nop instruction doesn’t do anything at runtime (except waste a CPU cycle), but it comes in handy when debugging as it allows the debugger to place a breakpoint on a line (instead of on a specific instruction). We can see this in action in a debug build: when placing the caret on the
nop instruction, the editor jumps to the curly brace which is where we could place a breakpoint.
(As an added bonus: there is more compiler magic in the above example. See that string “Hi there!” being concatenated from the original source code?)
When using the IL viewer in dotPeek, we can also configure it to display IL code as comments inside decompiled code. After enabling this in the options (under Decompiler | Show IL in comments), we can see this in action. Note that this only works for decompiled code – IL can not be displayed as a comment for code downloaded from external symbol servers.
The IL viewer in ReSharper and dotPeek can be used to learn about Intermediate Language and how our source code is compiled to it. When combined with a profiler, we can use it to optimize our code as well.