C# 7 Series, Part 2: Async Main

As you may know, there are two kinds of the programs the C# language can build. One is a program with an entrypoint so that the Operating System can load the program and execute it from the entrypoint; another is the program without an entrypoint. Operating System cannot directly execute the program, the program can be referenced by other program with an entrypoint, and the code can be then executed.

Application types that (must) have an entrypoint: Windows Forms App, UWP App, Console App, WPF App, ASP.NET and ASP.NET Core App and Xamarian App.

Application types that do not need an entrypoint: Class Library (/t:lib), Modules (/t:module).

The Main Method

Like other languages, C# program starts with the Main method. There are four overloads that are considered as the valid signatures for the Main method.

 public static void Main();
public static int Main();
public static void Main(string[] args);
public static int Main(string[] args);

The C# entrypoint method must be static, name of the method must be Main, return type of this method can be either void or int, it can have one parameter of a string array, containing any command-line arguments.

If the entrypoint returns an int, then the value can be evaluated by the Operating System, or the host process that started the program with this entrypoint, this is widely used in the native world (such as to indicate successful launch of an external app.)

The Async Main Method

C# introduced async/await pattern in version 5.0 (with .NET Framework 4.5), allowing easy-to-read workflow for an asynchronous operation. Many existing language constructs for those async operations (aka. async programming models) can work with the new async/await keywords. Today, community develops many libraries that only provide async version of the operations. (for example, System.Net.HttpClient, Microsoft.Azure.EventHub.Core. )

As I showed above, if you want to await an async operation in the entrypoint method, you need to apply some workaround, because the following entrypoint definition is invalid:

 public static async Task Main(string[] args) 
{ 
    await BuildWebHost(args).RunAsync(); 
}

image

The workaround would be synchronously wait for the operation.

 public static void Main(string[] args)
{     
    BuildWebHost(args).RunAsync().GetAwaiter().GetResult();
}

Or calling Wait() on the Task object:

 public static void Main(string[] args)
{
    BuildWebHost(args).RunAsync().Wait();
}

In C# 7.1, the language extends the valid signatures of an entrypoint to allow these async overloads of the Main method to be valid.

 public static void Main();
public static int Main();
public static void Main(string[] args);
public static int Main(string[] args);
public static Task Main();
public static Task<int> Main();
public static Task Main(string[] args);
public static Task<int> Main(string[] args);

A Task-like return type enables the await keyword with the async modifier in the entrypoint method.

image

Conclusion

The Async Main makes the asynchronous operations in the entrypoint method easier to use, without any workaround, like it runs in other async methods. In the compiler time, Any async Main method will be wrapped as a non-async Main method that can be accepted by the CLR Host. Since this feature does not correspond to a CLR code change, the async Main method is just a syntactical sugar. This design allows backend compatibility with the previous versions of the language. To read more details, please see Async Main in the Roslyn Git repo.