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.

Comments (12)

  1. Thanks! Awesome feature to have!

    ps: You have a small typo where you say “There are five overloads …”, when there are only four.

  2. Jason Bock says:

    “There are five overloads that are considered as the valid signatures for the Main method.” – you show four overloads. Which one is right?

  3. Alex Ivanoff says:

    How about overloads that take CancellationToken?

  4. Brent Miller says:

    Why don’t the signatures support CancellationToken?

    1. David Mercer says:

      > Why don’t the signatures support CancellationToken?

      Good question. Initially, I read it and thought, duh! What would generate the CancellationToken to be passed in? But then thinking about it more, I thought, why couldn’t the CLR Host, for instance, intercept a kill signal and trigger the CancellationToken.

      But then mazhou responded to another question:

      > The compiler will wrap your Main method into $MainEntrypoint$ and generate a traditional Main method that the CLR will accept and call GetAwaitor().GetResult().

      So it appears no changes were made to the CLR Host to accommodate this change, so there is nothing to generate or use a CancellationToken.

  5. Nate says:

    Great article! Didn’t know Main could be async in new C#.

    One tiny thing though, you wrote that there were five overloads of Main but listed four as below 🙂
    public static void Main();
    public static int Main();
    public static void Main(string[] args);
    public static int Main(string[] args);

  6. mazhou says:

    Thanks all of the great comments! Yes it is a typo, there are four overloads added instead of five. I will correct it later on the computer I used to compose the post, Web edit seems to break the format.

    For the ask for CancellationToken — I haven’t heard any plan to support it in the current C# release, to support cancelling, some changes may need to be made for the CLR Host. But it is never expensive to open an issue to ask the compiler team. They may have better view on this.

  7. enihcam says:

    help me to understand, if main() is async, who is waiting?

    1. mazhou says:

      If you have an async Main method, the compiler will generate some code like this:


      public static void Main(string[] args) => $MainEntrypoint$(args).GetAwaitor().GetResult();
      private static async Task $MainEntrypoint$(string[] args) { await ... }

      The compiler will wrap your Main method into $MainEntrypoint$ and generate a traditional Main method that the CLR will accept and call GetAwaitor().GetResult().

      Note the CLR is not changed to accept additional Main signatures, instead, compiler does the trick. Async Main is a syntactical sugar.

      1. Ricardo Rodrigues says:

        Like most things in C# it’s syntactic sugar, which is a good thing! Less code we have to write, less mistakes we make.

  8. jcotton42 says:

    “C# introduced async/await pattern in version 4.0,”

    Shouldn’t that say 5.0?

    1. mazhou says:

      Thanks! this is my bad, I will correct to C# 5.0.

Skip to main content