Welcome to C# 7.1

With C# we have always tended towards major releases: bundle a lot of features up, and release less frequently. We even went so far as routinely omitting the traditional ".0" when we talked about C# 6.0!

In the C# 7.0 "wave" we are trying something new. Tools such as Visual Studio upgrade on a frequent cadence, and there’s no longer a technical reason why C# couldn’t also be updated more frequently. So this time around we are embracing a notion of "point releases"; minor versions of C# that trickle out useful but smaller language features with shorter intervals. This means that you don’t have to wait so long for additional value to ship, but also makes it easier to align a C# release with the shipping of related features, e.g. in .NET.

Of course, upgrading to a new version of the language "all the time" can be a hassle in an organization, and isn’t for everyone. Visual Studio 2017 lets you decide whether to snap to the latest, or only to major (".0") versions of C#. You can choose your cadence.

The first point release

In August of 2017 we shipped the first point release of C#. It’s called C# 7.1, and its main purpose is really for us to get practice with point releases, without having too many dependencies on accompanying technology to complicate matters.

C# 7.1 is therefore a small release with just a few (but well-chosen) new language features; useful we think, but definitely in the minor range. It’s supported by Visual Studio 2017, starting with Update 15.3.

Let’s have a look! Here’s a program that uses the three C# 7.1 features we’re going to cover, plus a number of the recent C# 7.0 ones, just to make it interesting.

It computes the first 40 Fibonacci numbers in parallel on the thread pool, and prints them out in order.

Let’s look at each of the new C# 7.1 features used in here. For a full overview of the features in C# 7.1, check out the docs.

Async Main

The Main entry point method can now return a Task or a Task<int>. When it does, execution will wait for the returned task to complete, before shutting down the program.

Of course, the common use of this will be to make the Main method async, which you can also do:

    static async Task Main(string[] args)

This lets you await directly in the Main method, something you couldn’t do before.

    WriteLine($"Fib {tuple.input} = {await tuple.task}");

What you had to do previously was quite unappetizing: first you’d create an async helper method, MainAsync, say, with all the logic in. Then you’d write this cryptic Main method:

    static void Main(string[] args) => MainAsync().GetAwaiter().GetResult();

Now you can just make your Main method async, and the compiler will rewrite it for you.

Inferred tuple element names

In this lambda expression inside the query:

    input => (input, task: FibonacciAsync(input))

You notice that we create a tuple, but only give a name, task, for the second element. Yet a few lines later we are able to say

    WriteLine($"Fib {tuple.input} = {await tuple.task}");

Accessing the first element by the name tuple.input. That’s because when you create a tuple with an expression that "has" a name, like input in the lambda expression above, we’ll now automatically give the corresponding tuple element that name.

Default literals

If there’s an expected type for a default expression, you can now omit the mention of the type, as we do for the CancellationToken in in the signature of the FibonacciAsync method:

    private static Task<int> FibonacciAsync(int n, CancellationToken token = default)

This avoids tedious repetition of type names, or typing out long ones when they are already given by context.

What’s next?

We are already working on C# 7.2, as well as features that are intended for the next major release. If you are curious, you can follow along and participate at the C# language design GitHub repo: github.com/dotnet/csharplang.

Happy hacking!

Mads Torgersen, Lead Designer of C#