.NET 4.5 and C# 5 New Features

Content:

  1. Version numbers
  2. Release
  3. General
  4. Compatibility
  5. Big objects
  6. async & await
  7. New HttpClient class
  8. WinRT and Modern UI (Metro UI)

Version numbers:

.NET framework, .NET runtime, C# language/compiler and Visual Studio IDE each have their own version numbering scheme. For this release they are at 4.5, 4.0, 5.0 and 2012 respectively.

.NET framework .NET runtime C# language/compiler Visual Studio IDE Release date
1.0 1.0 1.0 2002 February 13th 2002
1.1 1.1 1.2 2003 April 24th 2003
2.0 2.0 2.0 2005 November 7th 2005
3.5 2.0 3.0 2008 November 19th 2007
4.0 4.0 4.0 2010 April 12th 2010
4.5 4.0 5.0 2012 August 15th 2012

Release:

.NET 4.5 and VS 2012 was released August 15th 2012.

General:

This update is different than previous updates.

In some ways this is the biggest update ever in .NET and C#. In other ways it is only minor updates.

Compatibility:

First an important note.

A system with only .NET 4.0 runtime (Windows 8 or 2012 out of the box) will per default not run .NET 2.0 and 3.5 applications.

Either .NET 3.5 must be installed (which will be suggested) or something must be added to app.config:

<configuration>
    <startup>
        <supportedRuntime version="v2.0.50727" />
        <supportedRuntime version="v4.0" />
    </startup>
</configuration>

or:

<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" />
  </startup>
</configuration>

The last one should be good for COM and/or mixed mode C++ - I do not have the details.

Big objects:

Previously max size for objects was 2 GB.

It can not be increased in app.config for .NET 4.5 for 64 bit Windows:

<configuration>
    <runtime>
       <gcAllowVeryLargeObjects enabled="true" />
    </runtime>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

Note that:

async & await:

By far the biggest change in C# 5.0 is the introduction of async and await.

And even though the idea in asynchroneous calls is simple, then these two keywords can be tricky to understand.

And it does not help that the choice of keywords are a bit misleading.

async does not make calls to the method asynchroneous - it just makes it possible to use await in the method.

await does not wait - instead it makes the execution asynchroneous.

So to make an asynchroneous call then one need to:

  1. Put async on method to enable the use of await
  2. Put await in where the asynchroneous execution should start

The C# compiler will then generate code where the awaited code will be executed in another thread while the method returns immediatetly.

Let us see some examples.

Normal synchroneous version:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy1
{
    public class Program
    {
        public static void Test()
        {
            Thread.Sleep(1000);
            Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
        }
        public static void Main(string[] args)
        {
            Test();
            Test();
            Test();
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

Output looks like expected:

Thread 1 done at 11:31:36.757
Thread 1 done at 11:31:37.769
Thread 1 done at 11:31:38.769
Calls completed at 11:31:38.769
Main done at 11:31:41.869

Now with async and await:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy2
{
    public class Program
    {
        public async static Task Test()
        {
            await Task.Delay(1000);
            Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
        }
        public static void Main(string[] args)
        {
            Test();
            Test();
            Test();
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

Output now looks like:

Calls completed at 11:37:03.154
Thread 4 done at 11:37:04.157
Thread 6 done at 11:37:04.178
Thread 5 done at 11:37:04.178
Main done at 11:37:06.265

Where we see that:

We also note that:

Sometimes it is necesarry to wait for something executing asynchroneously to complete. That can be done via the returned Task object:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy3
{
    public class Program
    {
        public async static Task Test()
        {
            await Task.Delay(1000);
            Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
        }
        public static void Main(string[] args)
        {
            Task t1 = Test();
            t1.Wait();
            Task t2 = Test();
            t2.Wait();
            Task t3 = Test();
            t3.Wait();
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

Now output looks like:

Thread 4 done at 11:42:08.110
Thread 4 done at 11:42:09.123
Thread 4 done at 11:42:10.137
Calls completed at 11:42:10.137
Main done at 11:42:13.237

But note that usually a lot of code will be executed between await return and Wait.

Task is also important when one need a value returned.

First attempt:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy4
{
    public class Program
    {
        public async static Task<int> Test()
        {
            await Task.Delay(1000);
            Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
            return 123;
        }
        public static void Main(string[] args)
        {
            int v1 = Test().Result;
            int v2 = Test().Result;
            int v3 = Test().Result;
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            Console.WriteLine("{0} {1} {2}", v1, v2, v3);
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

gives:

Thread 4 done at 11:45:19.336
Thread 4 done at 11:45:20.351
Thread 4 done at 11:45:21.363
Calls completed at 11:45:21.363
123 123 123
Main done at 11:45:24.463

Result does a Wait.

So:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy5
{
    public class Program
    {
        public async static Task<int> Test()
        {
            await Task.Delay(1000);
            Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
            return 123;
        }
        public static void Main(string[] args)
        {
            Task<int> t1 = Test();
            Task<int> t2 = Test();
            Task<int> t3 = Test();
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            int v1 = t1.Result;
            int v2 = t2.Result;
            int v3 = t3.Result;
            Console.WriteLine("{0} {1} {2}", v1, v2, v3);
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

which does the desired:

Calls completed at 11:47:13.962
Thread 4 done at 11:47:14.963
Thread 5 done at 11:47:14.964
Thread 6 done at 11:47:14.964
123 123 123
Main done at 11:47:18.064

To illustrate what happens behind the scene then we now do the same without async and awit:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy6
{
    public class Program
    {
        public static Task Test()
        {
            return Task.Run( () => {
                Thread.Sleep(1000);
                Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
            });
        }
        public static void Main(string[] args)
        {
            Test();
            Test();
            Test();
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

and:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Asy7
{
    public class Program
    {
        public static Task Test()
        {
            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
            Timer t = new Timer(cb => {
                Console.WriteLine("Thread {0} done at {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss.fff"));
                tcs.SetResult("not used");
            });
            t.Change(1000, -1);
            return tcs.Task;
        }
        public static void Main(string[] args)
        {
            Test();
            Test();
            Test();
            Console.WriteLine("Calls completed at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
            Thread.Sleep(3100);
            Console.WriteLine("Main done at {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }
}

Besides the compiler support Microsoft has:

TAP among other things recommends a naming convention that async methods should have an Async suffix in method name.

Example with WebClient:

using System;
using System.Net;
using System.Threading.Tasks;

namespace AsyIO1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            WebClient wc = new WebClient();
            for(int i = 0; i < 3; i++) 
            {
                DateTime dt1 = DateTime.Now;
                string google = wc.DownloadString("http://www.google.com/");
                DateTime dt2 = DateTime.Now;
                Console.WriteLine((int)(dt2 - dt1).TotalMilliseconds);
                DateTime dt3 = DateTime.Now;
                Task<string> th = wc.DownloadStringTaskAsync("http://www.google.com/");
                DateTime dt4 = DateTime.Now;
                string asygoogle = th.Result;
                DateTime dt5 = DateTime.Now;
                Console.WriteLine((int)(dt4 - dt3).TotalMilliseconds + " + " + (int)(dt5 - dt4).TotalMilliseconds);
            }
        }
    }
}

Output:

3230
10 + 231
158
0 + 224
154
0 + 159

We see that it is Result that takes the time not DownloadStringTaskAsync that completed immediatetly.

Example with StreamReader:

using System;
using System.IO;
using System.Threading.Tasks;

namespace AsyIO2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            using(StreamWriter sw = new StreamWriter(@"C:\work\big.txt") )
            {
                for(int i = 0; i < 10000000; i++)
                {
                    sw.WriteLine("12345678");
                }
            }
            for(int i = 0; i < 3; i++) 
            {
                StreamReader sr = new StreamReader(@"C:\work\big.txt");
                DateTime dt1 = DateTime.Now;
                string src = sr.ReadToEnd();
                DateTime dt2 = DateTime.Now;
                sr.Close();
                Console.WriteLine((int)(dt2 - dt1).TotalMilliseconds);
                StreamReader asysr = new StreamReader(@"C:\work\big.txt");
                DateTime dt3 = DateTime.Now;
                Task<string> th = asysr.ReadToEndAsync();
                DateTime dt4 = DateTime.Now;
                string asysrc = th.Result;
                DateTime dt5 = DateTime.Now;
                asysr.Close();
                Console.WriteLine((int)(dt4 - dt3).TotalMilliseconds + " + " + (int)(dt5 - dt4).TotalMilliseconds);
            }
        }
    }
}

Output:

476
79 + 6266
511
0 + 6200
533
0 + 6191

Again it is Result not ReadToEndAsync that takes the time.

New HttpClient class:

The new HttpClient class uses TAP.

Example:

using System;
using System.Net.Http;

namespace E
{
    public class Program
    {
        public static void Main(string[] args)
        {
            HttpClient hc = new HttpClient();
            String google = hc.GetStringAsync("http://www.google.com//").Result;
            Console.WriteLine(google);
        }
    }
}

This examples does not use await, but it can obviously be used.

I consider this to be a less important feature due to the existance of WebClient.

I predict that this will not be used much as most developers will just continue with WebClient.

WinRT and Modern UI (Metro UI):

Another big change in .NET 4.5 os the support for WInRT (and the new UI framework known as Metro but is actually named modern.

WinRT (Windows Runtime) is a new framework in Windows 8 and 2012. It is not the same as Windows RT, which is an ARM tablet version of Windows 8.

WinRT is native (unmanaged) code.

WinRT is COM based. WinRT components implements IUnknown like all COM components does and IInspectable which is specific for WinRT components. They do not implement IDispatch (used by VBS and JScript).

Meta data is not stored in a .tlb file like in the old days but instead in a .winmd file, which uses same format as .NET components.

.NET code can use WinRT components just like .NET components and .NET provide the required glue code.

WinRT is used by the socalled Modern UI apps.

Modern UI apps and traditional desktop apps are two very different programming models.

Illustrative figure: http://i.stack.imgur.com/y7uoI.png

But the change from C# WPF & SL to C# WinRT should be relative easy. Here are some tips: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/br229571.aspx

Article history:

Version Date Description
1.0 August 26th 2012 Initial version (in Danish) published on Eksperten.dk
2.0 July 14th 2016 Translation to English and complete reformatting and publishing here
2.1 October 8th 2016 Add content overview
2.2 March 23rd 2017 Add release dates

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj