Skip to main content

Introducing the Building Blocks of the .NET Platform (the CLR, CTS, and CLS)

Now that you know some of the major benefits provided by .NET, let’s preview three key (and interrelated) topics that make it all possible: the CLR, CTS, and CLS. From a programmer’s point of view, .NET can be understood as a runtime environment and a comprehensive base class library. The runtime layer is properly referred to as the Common Language Runtime, or CLR. The primary role of the CLR is to locate, load, and manage .NET objects on your behalf. The CLR also takes care of a number of low-level details such as memory management, application hosting, coordinating threads, and performing basic security checks (among other low-level details). 

Another building block of the .NET platform is the Common Type System, or CTS. The CTS specification fully describes all possible data types and all programming constructs supported by the runtime, specifies how these entities can interact with each other, and details how they are represented in the .NET metadata format. 

Understand that a given .NET-aware language might not support every feature defined by the CTS. The Common Language Specification, or CLS, is a related specification that defines a subset of common types and programming constructs that all .NET programming languages can agree on. Thus, if you build .NET types that expose only CLS-compliant features, you can rest assured that all .NET-aware languages can consume them. Conversely, if you make use of a data type or programming construct that is outside of the
bounds of the CLS, you cannot guarantee that every .NET programming language can interact with your .NET code library. Thankfully, as you will see later in this chapter, it is simple to tell your C# compiler to check all of your code for CLS compliance. 

The Role of the Base Class Libraries

In addition to the CLR, CTS, and CLS specifications, the .NET platform provides a base class library that is available to all .NET programming languages. Not only does this base class library encapsulate various primitives such as threads, file input/output (I/O), graphical rendering systems, and interaction with various external hardware devices, but it also provides support for a number of services required by most real-world applications. 

The base class libraries define types that can be used to build any type of software application. For example, you can use ASP.NET to build web sites and REST services, WCF to build distributed systems, WPF to build desktop GUI applications, and so forth. As well, the base class libraries provide types to interact with the directory and file system on a given computer, communicate with relational databases (via ADO.NET), and so forth. From a high level, you can visualize the relationship between the CLR, CTS, CLS, and the base class library.


What C# Brings to the Table

C# is a programming language whose core syntax looks very similar to the syntax of Java. However, calling C# a Java clone is inaccurate. In reality, both C# and Java are members of the C family of programming languages (e.g., C, Objective C, C++) and, therefore, share a similar syntax. 

The truth of the matter is that many of C#’s syntactic constructs are modeled after various aspects of Visual Basic (VB) and C++. For example, like VB, C# supports the notion of class properties (as opposed to traditional getter and setter methods) and optional parameters. Like C++, C# allows you to overload operators, as well as create structures, enumerations, and callback functions (via delegates). 

Moreover, as you work through this text, you will quickly see that C# supports a number of features traditionally found in various functional languages (e.g., LISP or Haskell) such as lambda expressions and anonymous types. Furthermore, with the advent of Language Integrated Query (LINQ), C# supports a number of constructs that make it quite unique in the programming landscape. Nevertheless, the bulk of C# is indeed influenced by C-based languages. 

Because C# is a hybrid of numerous languages, the result is a product that is as syntactically clean (if not cleaner) as Java, is about as simple as VB, and provides just about as much power and flexibility as C++. Here is a partial list of core C# features that are found in all versions of the language: 

  • No pointers required! C# programs typically have no need for direct pointer manipulation
  • Automatic memory management through garbage collection. Given this, C# does not support a delete keyword.
  • Formal syntactic constructs for classes, interfaces, structures, enumerations, and delegates.
  • The C++-like ability to overload operators for a custom type, without the complexity (e.g., making sure to “return *this to allow chaining” is not your problem).
  • Support for attribute-based programming. This brand of development allows you to annotate types and their members to further qualify their behavior. For example, if you mark a method with the [Obsolete] attribute, programmers will see your custom warning message print out if they attempt to make use of the decorated member.
With the release of .NET 2.0 (circa 2005), the C# programming language was updated to support numerous new bells and whistles, most notability the following:
  • The ability to build generic types and generic members. Using generics, you are able to build efficient and type-safe code that defines numerous placeholders specified at the time you interact with the generic item.
  • Support for anonymous methods, which allow you to supply an inline function anywhere a delegate type is required.
  • The ability to define a single type across multiple code files (or, if necessary, as an in-memory representation) using the partial keyword.
.NET 3.5 (released circa 2008) added even more functionality to the C# programming language, including the following features:
  • Support for strongly typed queries (e.g., LINQ) used to interact with various forms of data.
  • Support for anonymous types that allow you to model the structure of a type (rather than its behavior) on the fly in code.
  • The ability to extend the functionality of an existing type (without subclassing) using extension methods.
  • Inclusion of a lambda operator (=>), which even further simplifies working with .NET delegate types.
  • A new object initialization syntax, which allows you to set property values at the time of object creation.
.NET 4.0 (released in 2010) updated C# yet again with a handful of features.
  • Support for optional method parameters, as well as named method arguments.
  • Support for dynamic lookup of members at runtime via the dynamic keyword. this provides a unified approach to invoking members on the fly, regardless of which framework the member implemented.
  • Working with generic types is much more intuitive, given that you can easily map generic data to and from general System.Object collections via covariance and contravariance.
With the release of .NET 4.5, C# received a pair of new keywords (async and await), which greatly simplify multithreaded and asynchronous programming. If you have worked with previous versions of C#, you might recall that calling methods via secondary threads required a fair amount of cryptic code and the use of various .NET namespaces. Given that C# now supports language keywords that handle this complexity for you, the process of calling methods asynchronously is almost as easy as calling a method in a synchronous manner. 

C# 6 was released with .NET 4.6 and introduced a number of minor features that help streamline your codebase. Here are is a quick rundown of some of the features found in C# 6:

  • Inline initialization for automatic properties as well as support for read-only automatic properties
  • Single-line method implementations using the C# lambda operator
  • Support of static imports to provide direct access to static members within a namespace
  • A null conditional operator, which helps check for null parameters in a method implementation
  • A new string-formatting syntax termed string interpolation
  • The ability to filter exceptions using the new when keyword 
  • Using await in catch and finally blocks
  • nameOf expressions to return a string representation of symbols
  • Index initializers
  • Improved overload resolution
This brings me to the current major release of C#, which was released with .NET 4.7 in March 2017. Similar to C# 6, version 7 introduces additional features for streamlining your codebase, and it adds some more significant features (such as tuples and ref locals and returns) that developers have been asking to have included in the language specification for quite some time. These will be detailed throughout the remainder of this book; however, here is a quick rundown of the new features in C# 7:
  • Declaring out variables as inline arguments
  • Nesting functions inside other functions to limit scope and visibility
  • Additional expression-bodied members
  • Generalized async return types
  • New tokens to improve readability for numeric constants
  • Lightweight unnamed types (called tuples) that contain multiple fields 
  • Updates to logic flow using type matching in addition to value checking (pattern matching)
  • Returning a reference to a value, instead of just the value itself (ref locals and returns)
  • The introduction of lightweight throwaway varials (called discards)
  • Throw expressions, allowing the throw to be executed in more places, such as conditional expressions, lambdas, and others
Not long after C# 7 was released, C# 7.1 was released in August 2017. This minor release added the following features:
  • The ability to have a program’s main method be async
  • A new literal, default, that allows for initialization of any type.
  • Correction of an issue with pattern matching that prevented using generics with the new pattern matching feature.
  • Like anonymous methods, tuple names can be inferred from the projection that creates them. 
Managed vs. Unmanaged Code

It is important to note that the C# language can be used only to build software that is hosted under the .NET runtime (you could never use C# to build a native COM server or an unmanaged C/C++-style application). Officially speaking, the term used to describe the code targeting the .NET runtime is managed code. The binary unit that contains the managed code is termed an assembly (more details on assemblies in just a bit). Conversely, code that cannot be directly hosted by the .NET runtime is termed unmanaged code. 

As mentioned previously (and detailed later in this chapter and the next), the .NET platform can run on a variety of operating systems. Thus, it is quite possible to build a C# application on a Windows machine using Visual Studio and run the program on a macOS machine using the .NET Core runtime. As well, you could build a C# application on Linux using Xamarin Studio and run the program on Windows, macOS and so on. With the most recent release of Visual Studio 2017, you can also build .NET Core applications on a Mac to be run on Windows, macOS or Linux. To be sure, the notion of a managed environment makes it possible to build, deploy, and run .NET programs on a wide variety of target machines.


Comments