Udostępnij za pośrednictwem


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

  • Anonymous
    May 29, 2017
    Thanks! Awesome feature to have!ps: You have a small typo where you say "There are five overloads ...", when there are only four.
  • Anonymous
    May 30, 2017
    "There are five overloads that are considered as the valid signatures for the Main method." - you show four overloads. Which one is right?
  • Anonymous
    May 30, 2017
    How about overloads that take CancellationToken?
  • Anonymous
    May 30, 2017
    Why don't the signatures support CancellationToken?
    • Anonymous
      May 31, 2017
      The comment has been removed
  • Anonymous
    May 30, 2017
    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);
  • Anonymous
    May 30, 2017
    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.
  • Anonymous
    May 30, 2017
    help me to understand, if main() is async, who is waiting?
    • Anonymous
      May 31, 2017
      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.
      • Anonymous
        June 08, 2017
        Like most things in C# it's syntactic sugar, which is a good thing! Less code we have to write, less mistakes we make.
  • Anonymous
    June 07, 2017
    "C# introduced async/await pattern in version 4.0,"Shouldn't that say 5.0?
    • Anonymous
      June 07, 2017
      Thanks! this is my bad, I will correct to C# 5.0.
  • Anonymous
    June 06, 2018
    I still get Program does not contain a static 'Main' method suitable for an entry point in .NET Core 2.1 console application (using VS2017 preview 2)
    • Anonymous
      November 04, 2018
      @kofifus The blogger seems to have missed that it is a language feature of c# 7.1 and above. Visual Studio defaults to the Major version i.e. 7.0. You need to go into project properties -> build tab -> advanced -> language version, then choose C# 7.1 or higher.