.NET is best understood as a new runtime environment and a common base class library. To understand .NET, the roles of CLR, CTS and CLS must be understood.
CLR - Common Language Runtime: CLR is the runtime layer of .NET. The primary role of CLR is to locate, load, and manage .NET types. The CLR takes care of a number of low level details such automatic memory management, language integration, and simplified deployment and versioning of binary code libraries.
CTS - Common Type System: The CTS fully describes all possible data types supported by the runtime, specifies how these data types interact with each other, and details how they are represented in the .NET metadata format.
CLS- Common Language Specification: CLS is a set of rules that define a subset of common types that ensure .NET binaries can be used seamlessly across all languages targeting the .NET platform. If you build .NET types that are CLS-compliant, then all .NET-aware languages could make use of those types.
In addition to CLR, CTS, and CLS, .NET provides a base class library that is available to all .NET programming languages. This base class library encapsulates various primitives such as file IO, Data Access, Threading, XML/Soap. etc. as shown below:
C# is a new language specifically built for the .NET platform. C# offers the following features:
Note that C# is only capable of producing code that can execute within the .NET platform. Code targeting .NET is described as managed code. A binary unit that contains managed code is termed assembly.
Although .NET binaries take .DLL and .EXE extensions, they have absolutely no internal similarities to those classic extensions. Furthermore, .NET binaries are not described using IDL and are not registered on the system, and nor they contain platform-specific instructions. Rather .NET binaries internally contain platform-agnostic intermediate language called Microsoft Intermediate Language, or simply IL.
Note in the following graph that all .NET-aware languages emit IL instructions and metadata:
.NET-aware compilers produce binaries (DLL or EXE) that are called
assemblies. Assemblies are .NET binaries that contain IL, metadata,
and manifest data:
IL: Assemblies contain IL code that is not compiled
until absolutely necessary. Absolutely necessary is the point at
which a block of IL code is referenced for use by the .NET runtime engine.
Metadata: In addition to IL instructions, assemblies also contain metadata that describe in detail the characteristics of every type living within the binary. Think of this metadata as a dramatic improvement to COM's type libraries. However, unlike type libraries, metadata is always present and is automatically generated by a .NET-aware compiler.
Manifest: In addition to IL and metadata, assemblies themselves are described using metadata called manifest. The manifest contains information about the current version of the assembly, security constraints, locale information, and a list of all externally referenced assemblies that are required for proper execution. The following figure shows the contents of a sample manifest:
In general, there is a one-to-one correspondence between a .NET assembly and the underlying DLL or EXE. Thus if you are building a .NET DLL, it is safe to assume that the binary (DLL) and the assembly are one and the same. If an assembly is composed of a single EXE or DLL, then you have a single file assembly. Single file assembly contain all the necessary IL, metadata, and associated manifest in a single well-define package.
Multi file assemblies are composed of numerous .NET binaries, each of which is termed a module. When building multi file assemblies, one of those must contain the assembly manifest (and possible IL), while the other related modules contain nothing but raw IL and metadata.
MISL is a language that sits on top of any platform-specific instruction set. Regardless of which .NET-aware language you choose, the associated compiler emit IL instructions. This is true of all .NET compilers.
One benefit of compiling to IL is language integration. Because all .NET compilers emit IL instructions irrespective of the language being used, all languages are able to interact within a well defined binary arena.
IDL (in COM) is used to describe completely the types contained within a COM server. In addition to describing types, IDL can also describe characteristics about the server such as version, locale, etc. The problem with IDL is that not all information may be present, and it is the programmer's responsibly to ensure that IDL accurately reflects the internal types.
.NET framework does not use IDL at all but its spirit lives on. .NET metadata is used to describe each and every type defined in the binary (class, structure, enumeration, etc) as well as each members of each type (properties, methods, and events). Because of this, .NET binaries are completely self describing entities.
Metadata is used by various aspects of the .NET runtime as well as by various development tools (such as IntelliSense). Metadata is also used by various browsing utilities, debugging tools, and the C# compiler itself.
Because .NET assemblies contain IL instructions rather than platform-specific instructions, the underlying IL must be compiled on the fly before use. The entity that compiles IL instructions into platform-specific instructions is called Just-In-time (JIT) compiler.
.NET provides a JIT compiler for each CPU targeting the CLR. Therefore, developers can write a single body of code that be JIT-compiled and executed on machines with different architectures.
In .NET, a namespace is a logical grouping of related types under a unique name. Grouping types in a namespace provides a simple way to avoid name clashes between assemblies.
The term runtime can be considered as a collection of services to execute a block of code. The key difference between .NET runtime and other operating system runtimes is that .NET runtime provides a single well-defined runtime that is shared by all languages that are .NET aware.
The Common Language Runtime (CLR) consists of two main entities:
The following figure summarizes the workflow between a .NET source code, a .NET compiler, and the .NET execution engine:
Recall that CTS is a formal specification that defines how a specific type should be defined on order to be hosted by the CLR. A type can be a class, structure, interface, intrinsic data types, etc. Therefore, to build assemblies that can be used by all .NET aware languages, you need to conform the exposed types to the rules of CTS. The following is a review of the formal definitions of various members of the CTS:
Every .NET aware language supports the concept of a class type, which is the cornerstone of OOP As expected, CTS allows classes to support abstract members that provide polymorphic behavior. CTS-compliant classes may only derive from a single class (multiple inheritance is not allowed.) The following table summarizes a number of characteristics of interest to class types
.NET Class Characteristics | |
Class Characteristics | Meaning |
Is the class sealed? | Sealed classes cannot function as base classes to other classes |
Does the class implement interfaces? | Similar in concept to COM interfaces. CTS allows a class to implement any number of interfaces. |
Is the class abstract or concrete? | Abstract classes cannot be created (used to declare class interface - in the OO-sense). Concrete classes can be created directly. |
What is the visibility of the class? | Each class must be configured with a visibility attribute. This trait defines if a class can be used by external assemblies or only from within containing assemblies. |
User-Defined Data types (UDTs) have survived in .NET. CTS-compliant structures may have any number of parameterized constructors (the no-argument constructor is reserved.) All CTS-compliant structures are derived from a common base class: System.ValueType. Structures may not derived from other types; they are sealed.
An interface is a collection of abstract methods, properties and event definitions. Unlike COM interfaces, .NET interfaces do not derived from a common base interface such as IUnknown. When building a custom interface using a .NET-aware language, the CTS permits a given interface to be derived from multiple base interfaces.
Classes and structures can take any number of members. Formally, a member is from the set {method, property, field, event}. Note that CTS defines the various adornments that may be associated with a given type. For example, each member has a visibility trait (public, private, etc.) A member may be declared abstract, static, instance, etc.
Recall that enumerations allow you to group name/value pairs under a specific name. CTS requires that enumerations derive from a common base class: System.Enum.
.NET delegates are the equivalent of a type-safe C-style function pointer. The key difference is that a .NET delegate is a class that derived from MutiCastDelegate, rather than a raw memory address. These types provide a way for one entity to forward the call to another entity. Delegates provide the foundation of .NET event protocol.
.NET establishes a well-defined set of intrinsic data types. The key point to be aware of is that although a given language may use a unique keyword to declare a data type, all languages ultimately alias the same type defined in .NET class libraries.
CTS Intrinsic Data Types | |||
.NET Base Class | VB.NET Representation | C# Representation | C++ Representation |
System.Byte | Byte | byte | char |
System.SByte | Not Supported | sbyte | signed char |
System.Int16 | Short | short | short |
System.Int32 | Integer | int | int or long |
... |
Note that not all languages are able to represent the same data member of CTS. VB.NET for example has no support System.SByte ( a signed byte). It would be very useful to create a subset of CTS that defined a common shared set of programming constructs (and types) for all .NET-aware languages. There is where CLS comes in.
Different languages express different programming constructs in unique language-specific constructs - C# string concatenation uses + while VB.NET uses &. Languages also differ with their overall functionality - some languages allow overloading of specific types while others do not allow it. What is needed it to have a base line to which all .NET-aware languages are expected to conform.
CLS is a set of guideline that describe in detail the minimal and complete set of features that a .NET-aware complier must support to product code that can be hosted by the CLR, and at the same time be used in a uniform manner between all languages that target the .NET platform. CLS can be viewed as a subset of the full functionality defined by CTS.
Consider C++ code libraries such as MFC, ATL, and STL. These libraries give developers a well-defined set of code to leverage in applications. Unlike C++, C# does not come with a pre-defined set of language-specific classes - there is no C# class library. Rather, C# developers leverage existing types supplied by the .NET framework. To keep all the types within this binary well organized, .NET platform makes extensive use of the namespace concept.
The key difference between this approach and a language-specific library such as STL, is that any language targeting the .NET platform makes use of the same namespaces and same types as a C# developer.
The primary goal as a .NET developer is to get to know the wealth of types defined in the numerous base class namespaces. The most critical namespace to be familiar with is the System namespace. .NET offers many other namespaces - System.Data for data access, System.Collections for lists, queues, arrays, etc., System.Security, and many others ( As of Beta 2, I counted 98 different namespaces)
It is important to note that a namespace is nothing more than a convenient way to logically organize and understand related types. To access a namespace programmatically, the using directive must be used. Once a namespace has been referenced, you are free to create instances of the types they contain.
// C#
using System.Data;
using System.Web;
using System.Threading;
// C++
using namespace System.Data;
using namespace System.Web;
using namespace System.Threading;
The following techniques can be used to learn more about .NET namespaces: