C# Compilation to Machine Code
The HelloWorld program listing from Chapter 1 is obviously C# code, and you compiled it for execution using the C# compiler. However, the processor still cannot directly interpret compiled C# code. An additional compilation step is required to convert the result of C# compilation into machine code. Furthermore, the execution requires the involvement of an agent that adds additional services to the C# program, services that it was not necessary to code for explicitly.
All computer languages define syntax and semantics for programming. Since languages such as C and C++ compile to machine code, the platform for these languages is the underlying operating system and machine instruction set, be it Microsoft Windows, Linux, UNIX, or others. Languages such as C# are different; the underlying platform is the runtime (or VES).
CIL is what the C# compiler produces after compiling. It is termed a "common intermediate language" (CIL) because an additional step is required to transform the CIL into something that processors can understand. (Figure 18.1 shows the process.) In other words, C# compilation requires two steps:
Figure 18.1. Compiling C# to Machine Code
The runtime is able to understand CIL statements and compile them to machine code. Generally, a component within the runtime performs this compilation from CIL to machine code. This component is the just-in-time (JIT) compiler, and jitting can occur when the program is installed or executed. Most CLI implementations favor execution-time compilation of the CIL, but the CLI does not specify when the compilation needs to occur. In fact, the CLI even allows the CIL to be interpreted rather than compiled, similar to the way many scripting languages work. In addition, .NET includes a tool called NGEN that enables compilation to machine code prior to actually running the program. This preexecution-time compilation needs to take place on the computer that the program will be executing because it will evaluate the machine characteristics (processor, memory, and so on) in order to generate more efficient code. The advantage of using NGEN at installation (or at any time prior to execution) is that you can reduce the need for the jitter to run at startup, thereby decreasing startup time.