PDC10: The Future of C# and Visual Basic

At PDC 2000, we rolled out the .NET platform, including a new language called C#. A lot has happened since then! Each release has had a theme – in C# 2 we added generics; in C# 3 it was LINQ. Most recently in C# 4 with VS2010 we introduced deeper dynamic language support and expressed a commitment to feature parity across VB and C#.

Language designers need to observe and integrate major trends into their work. Over the last few years, we’ve seen several major trends: (i) declarative styles – where you say more about the ‘what’ and less about the ‘how’, e.g. DSLs, functional programming; (ii) the resurgence of dynamic languages like Ruby and JavaScript, and how that affects static typed languages; (iii) concurrency.

In the last major editions of the language, there has been significant investment in declarative styles and dynamic programming. But there isn’t heavy language support for concurrency today, even though there is good support in the framework through the likes of the Task Parallel Library.

One reason for the rise in concurrency is the rise in connected applications: it’s almost impossible today to build an application that doesn’t talk to a service, or indeed to build a service that doesn’t talk to another service. This increases latency – for example, it is just not acceptable for a UI to freeze while it waits for a service to return. As a result, we all practice the art of asynchronous programming. It is starting to become the norm, and we’re even starting to see the rise of async-only APIs in languages like JavaScript and Silverlight.

As a result, we’re looking at making concurrency the theme of the next release of our C# and Visual Basic languages.

The Challenges of Asynchronous Programming

In a synchronous call, the act of invoking a method and waiting for the result are fused: you can’t separate the two. With asynchronous programming, that changes. You can call the method and it will return immediately; later, it will deliver the result, so the two operations are separable.

There are a few problems with today’s model: (i) that the function can’t return the result, since the code is asynchronously executing, so it has to use a callback instead; (ii) it can be complex to update the UI without marshaling data back from a background thread; (iii) in a server setting, you don’t really want unconstrained thread creation because of the increased competition to the thread pool.

Anders demonstrated a simple example that shows how complex the asynchronous model quickly becomes. The code fragment below downloads and display movies based on a query of the NetFlix OData feed:

But this code is synchronous – the UI will hang while waiting for the call to return. Let’s follow through the steps needed to fix this:

We can replace the DownloadString() method with DownloadStringAsync();

Then we need to separate out the remaining code in the method to a handler for the DownloadStringCompleted event;

But how do we then return the result to the calling method? The QueryMovies method will already completed by this point. We can’t – instead, we need to make this method async as well, adding an Action<> delegate as a parameter.

Now we have to go back to the method that called QueryMovies and retrieve the result asynchronously . And this is where it really starts to get unpleasant, because QueryMovies is being called in a while loop, as follows:

void LoadMovies(int year) {

resultsPanel.Children.Clear();

statusText.Text = "";

var pageSize = 10;

var imageCount = 0;

while (true) {

var movies = QueryMovies(year, imageCount, pageSize);

if (movies.Length == 0) break;

DisplayMovies(movies);

imageCount += movies.Length;

}

statusText.Text = string.Format("{0} Titles", imageCount);

}

How do we pass a delegate to a while loop? It’s delegates all the way down. Instead we have to replace the while loop and reorder the code, as shown below:

...

action = movies => {

if (movies.Length > 0) {

DisplayMovies(movies);

imageCount += movies.Length;

QueryMovies(year, imageCount, pageSize, action);

}

else {

statusText.Text = string.Format("{0} Titles", imageCount);

}

...

As shown in the code above, you can do it – but it’s unwieldy, and the flow of the code gets lost. And this is before we’ve dealt with error handling! Clearly we can do better.

New Async Features

The Visual Studio Async CTP, announced at PDC, introduces some new extensions to VB and C# to support a simpler, more declarative approach to asynchronous development.

The Task Parallel Library introduced a Task<T> type, which is used to represent an ongoing operation: a value that is currently being computed and will be delivered at some time in the future. We can use this as the return type along with a new async keyword to let the compiler know how it will be used, as follows:

We now can add some asynchronous work to the class. The CTP introduces a number of Task<T>-centric extension methods that will eventually be added into the core framework, which enable us to compose and call other asynchronous methods seamlessly. For example, the call to DownloadString() in our original method can be replaced by a call to DownloadStringTaskAsync(), which returns Task<string> instead of just string. (This is something you can offer for your APIs too, of course.)

And we can now use the await operator, as follows:

var data = await client.DownloadStringTaskAsync(new Uri(url));

The compiler rewrites your code (as it does for iterators), to use a continuation and then return, executing the rest of the code when the result becomes available.

Now the LoadMovies() method is really simple. Starting with the original code above, we can again mark the header with the async keyword and then we can call the new async version of QueryMovies using the same await operator:

var movies = await QueryMoviesAsync(year, imageCount, pageSize);

That’s all you have to do! As you can see, the new syntax preserves the logical syntax of the code. Exception handling requires no additional work: we don’t need to worry about callbacks, for instance.

Background and Foreground Threads

It’s important to note that the example shown above does not use a background thread – the async calls are happening on the UI thread. They aren’t computationally expensive; they are just blocking on a network call.

What if there is something that is computationally expensive that you do want to run on a background thread? You can simply use a lambda within an await context, for example:

async void ComputeStuffAsync() {

double result = 0;

await TaskEx.Run(() => {

for (int i=1; i < 500000000; i++) {

result += Math.Sqrt(i);

}

});

MessageBox.Show("The result is " + result, "Background Task",

MessageBoxButton.OK, MessageBoxImage.Information);

}

In summary, what the new asynchronous support offers is an abstraction layer that unifies computational, network and I/O asynchrony. For more information on the features, you can read a whitepaper on the MSDN site. There are also a number of good samples in the CTP, including the example demonstrated above.