Microsoft intermediate language (MSIL) is the programming language of the Common Language Runtime (CLR) and the Common Instruction Language (CIL) for managed code. A managed application undergoes two compilations. The first compilation is from source code to MSIL and is performed by the language compiler. The second compilation occurs at run time, when the MSIL is compiled to native code. The CLR orchestrates the second compilation as part of process execution. The CLR is blind to the original source code. From the perspective of the CLR, managed applications are simply MSIL code and metadata. For this reason, .NET is considered language-agnostic, or independent. The process execution of a managed application is identical regardless of the source language, such as C# or Microsoft Visual Basic .NET.
MSIL promotes the concept of compile-once-and-run-anywhere in the .NET environment. Just-in-time (JIT) compilers, otherwise known as jitters, compile assemblies into native binary that targets a specific platform. You can write an application or component once and then deploy the application to Microsoft Windows, Linux, and other environments in which a compliant .NET platform is available. Prior to .NET, vendors maintained different versions of their product, which is costly and time-consuming. Another advantage to compile-once-and-run anywhere is the ability to assemble applications from components deployed on disparate hardware and platforms. This was one of the objectives of component technologies such as Component Object Model (COM) and Common Object Request Broker Architecture (CORBA), but it was never truly realized. .NET makes this a reality. If platform-agnostic code is a design goal for an application, best practices must be adopted to insulate the program from platform-specific code. This includes avoiding or isolating interoperability and calls to native application programming interfaces (APIs).
MSIL is a full-featured, object-oriented programming (OOP) language. However, there are some differences when compared with C# programming. For example, global functions are allowed in MSIL, but not supported in the C# language. Despite being a lower-level language, MSIL has expanded language elements. It encompasses the constituents common to most object-oriented languages: classes, structures, inheritance, transfer-of-control statements, an assortment of arithmetic operators, and much more. Indeed, you can write .NET applications directly in MSIL.
This is a book on C# programming. In that context, why is understanding MSIL important? An understanding of MSIL code advances a deeper comprehension of C# programming and .NET in general. It isn't just magic. MSIL removes much of the mystery and helps C# developers better maintain, debug, and write efficient code.
Managed applications, particularly production applications purchased from third-party vendors, are sometimes deployed without the original source code. How is an application maintained without the source code? For a managed application, as part of the assembly, the MSIL code is always available. The exception is when the assembly is obfuscated. Several tools, including Intermediate Language Disassembler (ILDASM), can disassemble an assembly and provide the MSIL code. With the MSIL code, a developer can essentially read the application. You can even modify the code as MSIL and reassemble the application. This is called roundtripping. Of course, this assumes that the developer is erudite in MSIL programming.
In a native application, debugging without a debug file (.pdb) is a challenge (which is an understatement). Debugging a native application without symbol files invariably meant interpreting assembly code. That was the challenge. More than a visceral understanding of assembly programming is needed to debug without symbol files. MSIL can be viewed as the assembly code of the CLR. Debugging a managed application without the germane symbol files requires more than a superficial understanding of MSIL. However, when compared to the tedious task of reading assembly, MSIL is a leisure cruise.
MSIL is instructive in managed programming. Learning MSIL programming is learning C# programming. What algorithms are truly efficient? When has boxing occurred? Which source code routines expand the footprint of the application? These secrets can be found in understanding MSIL code.
Inline MSIL is mentioned in numerous programming blogs, but is not currently available. I am an advocate of inline MSIL for C#. MSIL is more than an abstraction of higher-level source code. There are unique features in MSIL code that are not exposed in C#. In addition, C# is a code generator that emits MSIL code. In rare circumstances, the MSIL may not be as ideal for your specific application. For these reasons, I favor inline MSIL. However, the problem with inline MSIL is maintaining safe code. Inline MSIL is inherently unsafe and could lead to abuse. If a safe implementation of inline MSIL is not possible, it should not be added to the language. As mentioned, managed applications incur two compilations. First the language compiler and then the run time (jitter) compiles the application. You can compile MSIL code directly into an assembly with the MSIL compiler Intermediate Language Assembler (ILASM). Conversely, you can disassemble an assembly by engaging the MSIL disassembler (ILDASM).
This chapter is an overview of MSIL programming, not a comprehensive narrative on MSIL. The intention is to convey enough information on the language to aid in the interpretation, maintenance, and debugging of C# applications. For an authoritative explanation of MSIL, I recommend Inside Microsoft .NET IL Assembler, written by Serge Lidin (Microsoft Press, 2002). Serge is one of the original architects of the ILASM compiler, the ILDASM disassembler, and other tools included in the .NET Framework. Alternatively, consult the European Computer Manufacturers Association (ECMA) documents pertaining to CIL, which are available online at http://www.ecma-international.org/publications/standards/Ecma-335.htm.