The Common Language Runtime (CLR) is a complete, high level virtual machine designed to support a broad variety of programming languages and interoperation among them.
All interesting programs need some runtime library that allows them to interact with the other resources of the machine (such as user input, disk files, network communications, etc).
The program also needs to be converted in some way (either by interpretation or compilation) to a form that the native hardware can execute directly.
The CLR fixes problems like these by defining a very complete specification.
Thus, among other things, the CLR specifies:
Multi-language Support
In short, the runtime is a complete specification of the exact bits one has to put in a file to create and run a program.
The virtual machine that runs these files is at a high level appropriate for implementing a broad class of programming languages.
This virtual machine, along with an ever growing body of class libraries that run on that virtual machine, is what we call the common language runtime (CLR).
Some important but often overlooked ease of use features include:
The runtime has many features, so it is useful to categorize them as follows:
Garbage collection is a wonderful user feature because it simplifies programming. The most obvious simplification is that most explicit delete operations are no longer necessary.
While removing the delete operations is important, the real value to the programmer is a bit more subtle:
CLR needs to track all references to the GC heap almost all the time.
Code that does the extra bookkeeping so that it can report all of its live GC references "almost all the time" is called managed code (because it is "managed" by the CLR).
Code that does not do this is called unmanaged code.
Thus all code that existed before the CLR is unmanaged code, and in particular, all operating system code is unmanaged.
A GC is necessary to provide memory safety guarantees.
Effectively, the only way you have to determine if a delete is correct is to check it at runtime.
This is exactly what a GC does (checks to see if memory is still live).
Thus, for any programs that need heap-style memory allocations, if you want to guarantee memory safety, you need a GC.
In practice, the number of run-time checks needed is actually very small:
Run-time dispatch logic could have been implemented using primitive CIL instructions without direct support in the runtime, it would have suffered from two important disadvantages:
It is important to keep in mind that while the runtime supports these object-oriented concepts, it does not require their use. Languages without the concept of inheritance (e.g., functional languages) simply don't use these facilities.
The key characteristics of value types are:
Exceptions are a language feature that allow programmers to throw an arbitrary object at the point that a failure occurs.
When an object is thrown, the runtime searches the call stack for a method that declares that it can catch the exception.
If such a catch declaration is found, execution continues from that point.
The main reason is that parameterized types make programming easier.
The reason for this is subtle. The easiest way to see the effect is to imagine what a class library would look like if all types were replaced with a generic Object type.
This effect is not unlike what happens in dynamically typed languages like JavaScript.
In such a world, there are simply far more ways for a programmer to make incorrect (but type-safe) programs.
System.Reflection interface allows you to explore almost all aspects of a program (what types it has, the inheritance relationship, and what methods and fields are present).
In addition to simply inspecting programs at run time, it is also possible to perform operations on them (e.g., invoke methods, set fields, etc.).
In fact, the runtime libraries use this capability to generate code for "serializing" objects to store in a file or send across the network.
There are two main "flavors" of interoperation:
Сode that is generated from the CIL can be saved in a file using a tool called crossgen (similar to .NET Framework NGEN tool).
This avoids large amounts of compilation time at run time and is very important because the class library is so large.
CLR is responsible for creating the necessary threads to do the work.
Phew! The runtime does a lot! It has taken many pages just to describe some of the features of the runtime, without even starting to talk about internal details. The hope is, however, that this introduction will provide a useful framework for a deeper understanding of those internal details. The basic outline of this framework is: