Click here to Skip to main content
15,867,308 members
Articles / All Topics

.NET Backward Compatibility – Part 1

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
8 Jul 2016MIT5 min read 16.3K   7   3
History of .NET versioning: runtime (CLR), base libraries (BCL) and the C# and VB languages

The history of .NET versioning is a complicated one, even when excluding the recent developments regarding .NET Core. Let’s start off by clarifying which components we are talking about: The .NET runtime (CLR), the base libraries (BCL) and the C# language (and VB.NET) are all versioned independently but released together as the .NET Framework, usually in combination with a new Visual Studio release.

.NET Framework Visual Studio Included with Windows CLR BCL C# Major new feature
1.0 2002 1.0 1.0 1.0
1.1 2003 Server 2003 1.1 1.1 1.2
2.0 2005 2.0 2.0 2.0 Generics
3.0 Vista 2.0 3.0 2.0 WPF
3.5 2008 7 2.0 3.5 3.0 LINQ
4.0 2010 4.0 4.0 4.0 dynamic keyword, optional parameters
4.5 2012 8 4.0 4.5 5.0 async keyword
4.5.1 2013 8.1 4.0 4.5.1 5.0
4.6 2015 10 4.0 4.6 6.0 Null-conditional operator

When you select which version of the .NET Framework you wish to “target” in a Visual Studio project, you are effectively setting the version of the CLR and BCL but not the compiler. This means you can use a newer Visual Studio release to compile code that will run on older .NET Framework versions while still using new C#/VB language features. For example, with the C# 3.0 compiler distributed with Visual Studio 2008, you can use auto-properties (compiler-generated backing fields for properties) while producing output that will run on .NET Framework 2.0.

Visual Studio Multi Targeting

Many interesting features require support in both the compiler and the BCL. Let’s look at Language Integrated Queries (LINQ), introduced in the .NET Framework 3.5. LINQ adds the ability use functional-style programming and to translate C#/VB expressions to query languages like SQL. This is enabled by a number of other prerequisite features: lambda expressions, extension methods, the query syntax and expression trees.

Lambda expressions are basically just a more concise syntax for anonymous methods. Instead of

C#
myList.ForEach(delegate(string x) { return Console.WriteLine(x); });

you can now write:

C#
myList.ForEach(x => Console.WriteLine(x));

This is a compiler feature introduced in C# 3.0 and requires no changes to the CLR or BCL. Note that the lambda-version of the code sample did not need to explicitly specify string due to type inference. LINQ uses this to make long method chains with functional-style transformations much more readable.

Extension methods allow you to define new methods that appear to be part of existing types without having to modify them. You define a regular static method in a static class and annotate the method’s first argument with the this keyword, like this:

C#
static class MyExtensions
{
  public static int CountOccurencesOfLetter(this string value, char letter)
  {
    return value.Split(letter).Length - 1;
  }
}

Any instances of the type of this first argument now appear to have a method with the remaining arguments as an instance method:

C#
string word = "banana";
Console.WriteLine(word.CountOccurencesOfLetter('a'));

Calls to extension methods are compiled to regular static method invocations by the C# 3.0 compiler, making this a form of syntactic sugar. A new attribute called ExtensionAttribute was added to the BCL, which is used by the compiler to mark and detect extension methods in compiled code. So if you were to decompile the code defined above, the result would look something like this:

C#
static class MyExtensions
{
  [Extension]
  public static int CountOccurencesOfLetter(string value, char letter)
  {
    return value.Split(letter).Length - 1;
  }
}
 
// ...
 
string word = "banana";
Console.WriteLine(MyExtensions.CountOccurencesOfLetter(word, 'a'));

LINQ uses extension methods to add a whole host of methods to the IEnumerable<T> interface without requiring each implementation of the interface to provide these methods itself.

The query syntax allows you to replace method chains like this:

C#
var result = list.Where(x => x.Id > 2).Select(x => x.Name);

with a more SQL-like syntax:

C#
var result = from x in list where x.Id > 2 select x.Name;

This C# 3.0 feature works as long as list provides the appropriate method signatures for Where(), Select(), etc. (directly or via extension methods). It does not depend on any specific types in the BCL.

Expression trees are the final ingredient for LINQ. When a lamda expression is assigned to a variable or method argument of the type Expression<T> the compiler generates a tree-like data structure representing what the code would do rather than compiling it to actual IL code. LINQ uses expression trees to convert method calls on IQueryable<T> to SQL queries rather than directly executing the specified lambdas like with IEnumerable<T>.

Now that we have seen what LINQ can do for us, we don’t want to miss out on it. But for deployment tools like Zero Install, and especially its Bootstrapper, it is important to have the EXE run out-of-the-box on as many machines as possible. This means it is still advantageous to target .NET Framework versions as old as 2.0.

Fortunately, we can have our cake and eat it. The fantastic LinqBridge project backports most of LINQ to .NET 2.0. It does this by taking advantage of the separate versioning of the C#/VB compilers and the BCL we mentioned in the beginning of this article. When using features that depend on BCL classes, the compiler does not look in specific assemblies. Instead, it looks for specific namespaces in all referenced assemblies. The LinqBridge library provides alternate implementations of things like the ExtensionAttribute and the IEnumerable<T> extension methods. By targeting the .NET Framework 2.0 BCL and referencing LinqBridge with a C# 3.0 compiler, we get LINQ support without depending on the .NET Framework 3.5 on the target machine. LinqBridge does not provide support for expression trees. Since Zero Install does not use any databases, this is still a perfect fit for us.

In the next part, we’ll look at .NET 4.x features and targeting multiple .NET versions.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIncorrect language versions Pin
Paulo Morgado6-Jul-16 15:47
professionalPaulo Morgado6-Jul-16 15:47 
AnswerRe: Incorrect language versions Pin
Bastian Eicher8-Jul-16 1:44
professionalBastian Eicher8-Jul-16 1:44 
GeneralRe: Incorrect language versions Pin
Paulo Morgado10-Jul-16 0:36
professionalPaulo Morgado10-Jul-16 0:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.