For the first time since 2003, Microsoft is considering a point release for C#. Currently marked as C# 7.1, the next version of the language is expected to include Async Main, Default Expressions, Infer Tuple Names, and Pattern-matching with Generics.

Async Main

Often frustrating for developers who are experimenting with asynchronous code, is the fact that console applications currently don’t support an asynchronous entry point. The workaround is a couple lines of boilerplate code, but the pattern isn’t obvious because it relies on methods that are not normally used.

public static void Main() { MainAsync().GetAwaiter().GetResult(); } private static async Task MainAsync() { ... // Main body here }

To address this, the Async Main proposal simply adds four new signatures to the list of possible entry points.

static Task Main() static Task<int> Main() static Task Main(string[]) static Task<int> Main(string[])

If one of these entry point functions is present then the compiler will generate the necessary boilerplate, unless you also have a non-async Main function. This last restriction is needed for backwards compatibility.

They had considered allowing “async void Main()”, but that made the compiler more complicated and in general Microsoft wants to discourage the use of “async void” outside of event handlers.

Default (i.e. Nothing)

One of the subtle differences between C# and VB is that VB doesn’t have a keyword that means “null”. There is the keyword “Nothing”, but that is described in the language specification as,

Nothing is a special literal; it does not have a type and is convertible to all types in the type system, including type parameters. When converted to a particular type, it is the equivalent of the default value of that type.

Currently C# uses the pattern “default(T)” to get the same effect, but that can become a bit tedious, especially with long class names. With C# 7.1, we’ll have a “default literal” described as,

An expression with this classification can be implicitly converted to any type, by a default-or-null literal conversion.

The inference of the type for the default literal works the same as that for the null literal, except that any type is allowed (not just reference types).

The default literal can be used in most places where you could have used null. This is seen as a drawback in the proposal, presumably because it is generally frowned upon to have two very similar ways of doing the same thing. The design meeting notes ask,

Are we setting up for style wars?

The following example uses of the default literal:

ImmutableArray<SomeType> x = default; return default; void Method(ImmutableArray<SomeType> arrayOpt = default) var x = new[] { default, ImmutableArray.Create(y) }; const int x = default; if (x == default) if (x is default) y = default as RefType //compiler warning: always null int i = default

The following are illegal uses of the default literal:

const int? y = default; if (default == default) if (default is T) var i = default throw default

The last one is a weird artifact of the C# design. From the design meeting notes,

You are allowed to throw null in C#. It leads to a runtime error, which ironically causes a NullReferenceException to be thrown. So it is a sneaky/ugly idiom for throwing a NullReferenceException. There's no good reason to allow this for default. We don't think the user has any intuition that that should work, or about what it does.

Instead of introducing the default literal, they considered simply extend “null” to have the same effect. VB can do this because “nothing” and “null” are different words. VB still has the concept of a null even without the keyword; so you see things like a “NothingReferenceException”.

In C#, you would be constantly asking, “Do you mean null as in actually null or null as in the default value which may or may not be null?”. The conclusion was that it was too confusing.

In Part 2 we’ll look at tuples and pattern matching.