Click here to Skip to main content
15,886,031 members
Articles / All Topics

.NET Backward Compatibility – Part 2

Rate me:
Please Sign up or sign in to vote.
4.55/5 (3 votes)
8 Jul 2016MIT5 min read 10K   9   2
Different versions of the .NET Framework are considered in-place upgrades of each other.

In the previous part, we talked about how different parts of the .NET Framework are versioned and how we can backport parts of it.

Different versions of the .NET Framework are considered in-place upgrades of each other when they share the same CLR. Versions based on different CLRs can be installed side-by-side. Let’s look at the table from the last post again:

.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

So .NET Framework 3.5 replaces 2.0 and 3.0 while 4.6 replaces 4.0, 4.5 and 4.5.1. The post Introduction to .NET Framework Compatibility on the .NET Blog provides some great insight into how the .NET Framework ensures application compatibility when such upgrades are performed. Versions 1.0 and 1.1 of the .NET Framework are unsupported on current Windows versions and have become pretty much obsolete.

Looking at the table one more time, you can see that Windows 8 is the first release to bundle a .NET Framework version that uses the CLR 4.0. A side-by-side installation of the .NET Framework 3.5 is available as an optional component but not installed by default. When you try to run a .NET 2.0/3.x application on a fresh installation of Windows 8, 8.1 or 10 you are presented with a dialog like this:

.NET Framework 3.5 as an optional Windows component

Windows Vista and 7 on the other hand bundle .NET Framework versions with the CLR 2.0. So how do we create a single .NET executable that “just works” on all popular Windows versions without requiring the user to download additional components? By adding this to the project’s App.config:

XML
<startup>
  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
  <supportedRuntime version="v2.0.50727" />
</startup>

When compiling a project, Visual Studio places the App.config file next to the generated executable and names it like MyApp.exe.config. When launching a .NET executable, the system looks for such .config files before executing any of the EXE’s actual code. The <supportedRuntime> tags tell the system which CLRs the application may be run in, regardless of the .NET Framework version it targeted at build time. This way, we get the best of both worlds: Our executable runs on the CLR 4.0 when available but also works on the CLR 2.0. However, it is now our responsibility to make sure we do not depend on any behaviors or quirks that changed between the two CLR versions.

Now, let’s take a look at the async keyword introduced in the .NET Framework 4.5. This feature simplifies writing callback-based asynchronous code. Here’s an example:

C#
private async Task WaitAsync()
{
    var client = new HttpClient();
    Task<string> task = client.GetStringAsync("http://0install.de/");
    string result = await task;
    MessageBox.Show(result);
}

The async keyword in the method signature tells the compiler to expect await calls in the method body. These mark points where the sequential execution of a method ends and the following code is transformed into a callback. Such callbacks are used to resume the execution when a blocking operation represented by a Task object (e.g. an HTTP request) has been completed. Notice the return type of the WaitAsync() method itself is also Task even though it has no return statements. The compiler implicitly returns Tasks at await points, allowing consumers of your method to again use await.

The code we wrote above is transformed by the compiler into something like this:

C#
private Task WaitAsync()
{
    var client = new HttpClient();
    Task<string> task = client.GetStringAsync("http://0install.de/");
    task.ContinueWith(t =>
    {
        string result = t.Result;
        MessageBox.Show(result);
    });
}

Asynchronous methods often provide an overload with an argument of the type CancellationToken. These tokens can be used to request cooperative cancellation. This means that the asynchronous method checks for pending cancellation requests at fixed points and has the opportunity to clean up after itself. Another common argument type for asynchronous methods is the IProgress<T> interface. Callers can use this to provide a callback for tracking the progress of long-running operations.

As you can see, the async feature, much like LINQ, depends on specific classes and methods being present in the BCL (or any other library) while leaving most of the “magic” to the compiler. Microsoft provides a NuGet package called Microsoft.Bcl.Async that backports the Async-related BCL classes introduced in the .NET Framework 4.5 to the .NET Framework 4.0. This is similar to the LinqBridge library we talked about in the previous post.

We started developing Zero Install for Windows in 2010, 2 years before .NET Framework 4.5 was released in 2012. This led us to implement our own system for managing asynchronous tasks based on classic threads rather than callbacks. We extracted this functionality from Zero Install to a shared utils library called NanoByte.Common. We’ll talk about this in more detail in a future post.

After the release of the .NET Framework 4.5, we refactored the cancellation- and progress-related classes of our task code (not the actual task execution though) to match the signatures of the equivalent .NET classes as closely as possible. Unfortunately, we could not simply use the backported classes from the NuGet package mentioned above. Unlike their Microsoft-provided equivalents our classes are serializable, making them compatible with .NET Remoting. While this feature is considered legacy and has been mostly superseded by WCF, it is still very useful for IPC between applications and services running on the same machine. In Zero Install, we use it for communicating with the Store Service. For easier interoperability, we added extension methods and implicit type casts that convert between our custom classes and their .NET Framework counterparts when NanoByte.Common is compiled for .NET 4.5 or later.

In the next part, we’ll look at library versioning in .NET, before talking about targeting multiple .NET Framework versions in the last part.

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

 
Questiona subject Pin
T5Martin4-Jul-16 21:14
T5Martin4-Jul-16 21:14 
AnswerRe: a subject Pin
Bastian Eicher5-Jul-16 10:44
professionalBastian Eicher5-Jul-16 10:44 

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.