- What Is .NET?
- The Common Language Runtime (CLR)
- The .NET Framework Class Library (FCL)
- C# and Other .NET Languages
- The Common Type System (CTS)
- The Common Language Specification (CLS)
- Summary
The Common Language Runtime (CLR)
As introduced in the preceding section, C# applications are compiled to IL, which is executed by the CLR. This section highlights several features of the CLR. You'll also see how the CLR manages your application during execution.
Why Is the CLR Important?
In many traditional execution environments of the past, programmers needed to perform a lot of the low-level work (plumbing) that applications needed to support. For example, you had to build custom security systems, implement error handling, and manage memory.
The degree to which these services were supported on different language platforms varied considerably. Visual Basic (VB) programmers had built-in memory management and an error-handling system, but they didn't always have easy access to all the features of COM+, which opened up more sophisticated security and transaction processing. C++ programmers have full access to COM+ and exception handling, but memory management is a totally manual process. In a later section, you learn about how .NET supports multiple languages, but knowing just a little about a couple of popular languages and a couple of the many challenges they must overcome can help you to understand why the CLR is such a benefit for a C# developer.
The CLR solves many problems of the past by offering a feature-rich set of plumbing services that all languages can use. The features described in the next section further highlight the value of the CLR.
CLR Features
This section describes, more specifically, what the CLR does for you. Table 1.1 summarizes CLR features with descriptions and chapter references (if applicable) in this book where you can find more detailed information.
Table 1.1. CLR Features
Feature |
Description |
.NET Framework Class Library support |
Contains built-in types and libraries to manage assemblies, memory, security, threading, and other runtime system support |
Debugging |
Facilities for making it easier to debug code. (Chapter 7) |
Exception management |
Allows you to write code to create and handle exceptions. (Chapter 11) |
Execution management |
Manages the execution of code |
Garbage collection |
Automatic memory management and garbage collection (Chapter 15) |
Interop |
Backward-compatibility with COM and Win32 code. (Chapter 41) |
Just-In-Time (JIT) compilation |
An efficiency feature for ensuring that the CLR only compiles code just before it executes |
Security |
Traditional role-based security support, in addition to Code Access Security (CAS) (Chapter 44) |
Thread management |
Allows you to run multiple threads of execution (Chapter 39) |
Type loading |
Finds and loads assemblies and types |
Type safety |
Ensures references match compatible types, which is very useful for reliable and secure code (Chapter 4) |
In addition to the descriptions provided in Table 1.1, the following sections expand upon a few of the CLR features. These features are included in the CLR execution process.
The CLR Execution Process
Beyond just executing code, parts of the execution process directly affect your application design and how a program behaves at runtime. Many of these subjects are handled throughout this book, but this section highlights specific additional items you should know about.
From the time you or another process selects a .NET application for execution, the CLR executes a special process to run your application, shown in Figure 1.4.
Figure 1.4 The CLR execution process (summarized).
As illustrated in Figure 1.4, Windows (the OS) will be running at Start; the CLR won't begin execution until Windows starts it. When an application executes, OS inspects the file to see whether it has a special header to indicate that it is a .NET application. If not, Windows continues to run the application.
If an application is for .NET, Windows starts up the CLR and passes the application to the CLR for execution. The CLR loads the executable assembly, finds the entry point, and begins its execution process.
The executable assembly could reference other assemblies, such as dynamic link libraries (DLLs), so the CLR will load those. However, this is on an as-needed basis. An assembly won't be loaded until the CLR needs access to the assembly's code. It's possible that the code in some assemblies won't be executed, so there isn't a need to use resources unless absolutely necessary.
As mentioned previously, the C# compiler produces IL as part of an assembly's output. To execute the code, the CLR must translate the IL to binary code that the operating system understands. This is the responsibility of the JIT compiler.
As its name implies, the JIT compiler only compiles code before the first time that it executes. After the IL is compiled to machine code by the JIT compiler, the CLR holds the compiled code in a working set. The next time that the code must execute, the CLR checks its working set and runs the code directly if it is already compiled. It is possible that the working set could be paged out of memory during program execution, for various reasons that are necessary for efficient operation of the CLR on the particular machine it is running on. If more memory is available than the size of the working set, the CLR can hold on to the code. Additionally, in the case of Web applications where scalability is an issue, the working set can be swapped out due to periodic recycling or heavier load on the server, resulting in additional load time for subsequent requests.
The JIT compiler operates at the method level. If you aren't familiar with the term method, it is essentially the same as a function or procedure in other languages. Therefore, when the CLR begins execution, the JIT compiler compiles the entry point (the Main method in C#). Each subsequent method is JIT compiled just before execution. If a method being JIT compiled contains calls to methods in another assembly, the CLR loads that assembly (if not already loaded).
This process of checking the working set, JIT compilation, assembly loading, and execution continues until the program ends.
The meaning to you in the CLR execution process is in the form of application design and understanding performance characteristics. In the case of assembly loading, you have some control over when certain code is loaded. For example, if you have code that is seldom used or necessary only in specialized cases, you could separate it into its own DLL, which will keep the CLR from loading it when not in use. Similarly, separating seldomly executed logic into a separate method ensures the code doesn't JIT until it's called.
Another detail you might be concerned with is application performance. As described earlier, code is loaded and JIT compiled. Another DLL adds load time, which may or may not make a difference to you, but it is certainly something to be aware of. By the way, after code has been JIT compiled, it executes as fast as any other binary code in memory.
One of the CLR features listed in Table 1.1 is .NET Framework Class Library (FCL) support. The next section goes beyond FCL support for the CLR and gives an overview of what else the FCL includes.