Assíncrono principalAsync Main

  • [x] proposta[x] Proposed
  • [] Protótipo[ ] Prototype
  • [] Implementação[ ] Implementation
  • [] Especificação[ ] Specification

ResumoSummary

Permita que await seja usado no método Main/EntryPoint de um aplicativo, permitindo que o ponto de entrada retorne Task / Task<int> e seja marcado async.Allow await to be used in an application's Main / entrypoint method by allowing the entrypoint to return Task / Task<int> and be marked async.

MotivaçãoMotivation

É muito comum ao aprender C#, ao escrever utilitários baseados em console e ao escrever aplicativos de teste pequenos para desejarem chamar e await async métodos do principal.It is very common when learning C#, when writing console-based utilities, and when writing small test apps to want to call and await async methods from Main. Hoje, adicionamos um nível de complexidade, forçando tal await"a ser feito em um método assíncrono separado, o que faz com que os desenvolvedores precisem escrever clichê como o seguinte apenas para começar:Today we add a level of complexity here by forcing such await'ing to be done in a separate async method, which causes developers to need to write boilerplate like the following just to get started:

public static void Main()
{
    MainAsync().GetAwaiter().GetResult();
}

private static async Task MainAsync()
{
    ... // Main body here
}

Podemos remover a necessidade desse texto clichê e torná-lo mais fácil de começar simplesmente permitindo que o principal seja async de forma que awaits possa ser usado.We can remove the need for this boilerplate and make it easier to get started simply by allowing Main itself to be async such that awaits can be used in it.

Design detalhadoDetailed design

No momento, as seguintes assinaturas são de entryPoints permitidas:The following signatures are currently allowed entrypoints:

static void Main()
static void Main(string[])
static int Main()
static int Main(string[])

Estendemos a lista de entryPoints permitidos para incluir:We extend the list of allowed entrypoints to include:

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

Para evitar riscos de compatibilidade, essas novas assinaturas só serão consideradas como entryPoints válidos se não houver sobrecargas do conjunto anterior.To avoid compatibility risks, these new signatures will only be considered as valid entrypoints if no overloads of the previous set are present. O idioma/compilador não exigirá que o EntryPoint seja marcado como async, embora espere que a grande maioria dos usos será marcada como tal.The language / compiler will not require that the entrypoint be marked as async, though we expect the vast majority of uses will be marked as such.

Quando um deles é identificado como EntryPoint, o compilador sintetiza um método EntryPoint real que chama um desses métodos codificados:When one of these is identified as the entrypoint, the compiler will synthesize an actual entrypoint method that calls one of these coded methods:

  • static Task Main() resultará no compilador que emite o equivalente de private static void $GeneratedMain() => Main().GetAwaiter().GetResult();static Task Main() will result in the compiler emitting the equivalent of private static void $GeneratedMain() => Main().GetAwaiter().GetResult();
  • static Task Main(string[]) resultará no compilador que emite o equivalente de private static void $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();static Task Main(string[]) will result in the compiler emitting the equivalent of private static void $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();
  • static Task<int> Main() resultará no compilador que emite o equivalente de private static int $GeneratedMain() => Main().GetAwaiter().GetResult();static Task<int> Main() will result in the compiler emitting the equivalent of private static int $GeneratedMain() => Main().GetAwaiter().GetResult();
  • static Task<int> Main(string[]) resultará no compilador que emite o equivalente de private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();static Task<int> Main(string[]) will result in the compiler emitting the equivalent of private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();

Exemplo de uso:Example usage:

using System;
using System.Net.Http;

class Test
{
    static async Task Main(string[] args) =>
        Console.WriteLine(await new HttpClient().GetStringAsync(args[0]));
}

DesvantagensDrawbacks

A principal desvantagem é simplesmente a complexidade adicional de dar suporte a assinaturas de EntryPoint adicionais.The main drawback is simply the additional complexity of supporting additional entrypoint signatures.

AlternativasAlternatives

Outras variantes consideradas:Other variants considered:

Permitindo async void.Allowing async void. Precisamos manter a semântica o mesmo para o código chamando diretamente, o que dificultaria um ponto de entrada gerado para chamá-lo (nenhuma tarefa foi retornada).We need to keep the semantics the same for code calling it directly, which would then make it difficult for a generated entrypoint to call it (no Task returned). Poderíamos resolver isso gerando dois outros métodos, por exemplo,We could solve this by generating two other methods, e.g.

public static async void Main()
{
   ... // await code
}

torna-sebecomes

public static async void Main() => await $MainTask();

private static void $EntrypointMain() => Main().GetAwaiter().GetResult();

private static async Task $MainTask()
{
    ... // await code
}

Também há preocupações em relação à incentivação do uso de async void.There are also concerns around encouraging usage of async void.

Usando "MainAsync" em vez de "Main" como o nome.Using "MainAsync" instead of "Main" as the name. Embora o sufixo assíncrono seja recomendado para métodos de retorno de tarefas, trata-se principalmente da funcionalidade da biblioteca, que principal não é, e o suporte a nomes de EntryPoint adicionais além de "Main" não vale a pena.While the async suffix is recommended for Task-returning methods, that's primarily about library functionality, which Main is not, and supporting additional entrypoint names beyond "Main" is not worth it.

Perguntas não resolvidasUnresolved questions

N/Dn/a

Criar reuniõesDesign meetings

N/Dn/a