O uso do sufixo “Async” em um nome de método depende se o modificador 'async' é usado?

106

Qual é a convenção para sufixar nomes de método com "Async"?

O sufixo "Async" deve ser acrescentado apenas a um método declarado com o asyncmodificador?

public async Task<bool> ConnectAsync()

Ou basta que o método apenas retorne Task<T>ou Task?

public Task<bool> ConnectAsync()
Kasperhj
fonte
4
Para a parte de nomenclatura, o documento TAP diz: Os métodos assíncronos no TAP incluem o sufixo Async após o nome da operação; por exemplo, GetAsync para uma operação get. Se você estiver adicionando um método TAP a uma classe que já contém esse nome de método com o sufixo Async, use o sufixo TaskAsync. Por exemplo, se a classe já tiver um método GetAsync, use o nome GetTaskAsync.
James Manning
4
ok, acho que fiquei confuso com o título da pergunta "Convenção de nomenclatura para métodos assíncronos"
James Manning,
1
Esta é uma questão mal construída. Pessoas discutindo, respostas ambíguas.
Luke Puplett
4
Porque muitas pessoas não entenderam isso e estão discutindo sobre o que está sendo perguntado, se perguntando se é uma questão de duas partes etc. A prova de que é confuso é que as pessoas estão confusas.
Luke Puplett
2
@DavidRR Até hoje eu ainda não entendo a quantidade de confusão que essa pergunta aparentemente causou. Se suas edições trouxerem alguma ordem na confusão de tal forma que ela tenha ajudado você e possivelmente possa ajudar outros, então eu agradeço suas edições, pois você alcançou algo que eu não poderia na formulação original. A questão agora é tão antiga que mal consigo me lembrar de minha mentalidade quando a fiz aqui e, portanto, a intenção original é menos importante. A resposta de Lucas reflete que nem todos estavam confusos. Achei extremamente útil.
kasperhj

Respostas:

127

Acho que a verdade é ambígua até mesmo na documentação da Microsoft:

No Visual Studio 2012 e no .NET Framework 4.5, qualquer método que é atribuído com a asyncpalavra - chave ( Asyncno Visual Basic) é considerado um método assíncrono, e os compiladores C # e Visual Basic realizam as transformações necessárias para implementar o método de forma assíncrona usando TAP. Um método assíncrono deve retornar um Taskou um Task<TResult>objeto.

http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx

Isso já não está certo. Qualquer método com asyncé assíncrono e então diz que deve retornar um Taskou Task<T>- o que não é certo para métodos no topo de uma pilha de chamadas, Button_Click por exemplo, ou async void.

Claro, você deve considerar qual é o objetivo da convenção?

Você poderia dizer que a Asyncconvenção de sufixo é para comunicar ao usuário da API que o método está aguardando. Para um método ser aguardado, ele deve retornar Taskpara um void ou Task<T>para um método de retorno de valor, o que significa que apenas o último pode ser sufixado com Async.

Ou você pode dizer que a Asyncconvenção de sufixo é para comunicar que o método pode retornar imediatamente, renunciando ao thread atual para realizar outro trabalho e potencialmente causando corridas.

Esta citação de documento da Microsoft diz:

Por convenção, você anexa "Async" aos nomes dos métodos que têm um modificador Async ou async.

http://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_NamingConvention

O que nem mesmo menciona que seus próprios métodos assíncronos retornando Taskprecisam do Asyncsufixo, o que acho que todos concordamos que eles precisam.


Portanto, a resposta a essa pergunta poderia ser: ambos. Em ambos os casos, você precisa anexar Asynca métodos com asyncpalavra - chave e que retornam Taskou Task<T>.


Vou pedir a Stephen Toub que esclareça a situação.

Atualizar

Então eu fiz. E aqui está o que nosso bom homem escreveu:

Se um método público retornar uma tarefa e for assíncrono por natureza (ao contrário de um método que sempre executa de forma síncrona até a conclusão, mas ainda retorna uma tarefa por algum motivo), ele deve ter um sufixo “Async”. Essa é a diretriz. O objetivo principal aqui com a nomenclatura é tornar muito óbvio para um consumidor da funcionalidade que o método que está sendo chamado provavelmente não completará todo o seu trabalho de forma síncrona; é claro que também ajuda no caso em que a funcionalidade é exposta com métodos síncronos e assíncronos, de forma que você precisa de uma diferença de nome para distingui-los. Como o método atinge sua implementação assíncrona é irrelevante para a nomenclatura: se async / await é usado para obter a ajuda do compilador ou se os tipos e métodos de System.Threading.Tasks são usados ​​diretamente (por exemplo, g. TaskCompletionSource) não importa, pois isso não afeta a assinatura do método no que diz respeito ao consumidor do método.

Claro, sempre há exceções a uma diretriz. O mais notável no caso de nomenclatura seriam os casos em que a razão de ser de todo um tipo é fornecer funcionalidade com foco assíncrono, caso em que ter Async em todos os métodos seria um exagero, por exemplo, os métodos na própria Tarefa que produzem outras Tarefas .

Quanto aos métodos assíncronos de retorno de vazio, não é desejável tê-los em uma área de superfície pública, uma vez que o chamador não tem uma boa maneira de saber quando o trabalho assíncrono foi concluído. Se você precisar expor publicamente um método assíncrono de retorno vazio, provavelmente deseja ter um nome que transmita que o trabalho assíncrono está sendo iniciado e poderá usar o sufixo “Async” aqui, se fizer sentido. Dado o quão raro esse caso deve ser, eu diria que é realmente uma decisão caso a caso.

Espero que ajude, Steve

A orientação sucinta da frase inicial de Stephen é bastante clara. Ele exclui async voidporque é incomum querer criar uma API pública com esse design, pois a maneira correta de implementar um vazio assíncrono é retornar uma Taskinstância simples e deixar o compilador fazer sua mágica. No entanto, se você quiser um public async void, Asyncé aconselhável anexar . Outros async voidmétodos do topo da pilha , como manipuladores de eventos, geralmente não são públicos e não importam / qualificam.

Para mim, isso me diz que, se eu me perguntar sobre como sufocar Asyncem um async void, provavelmente devo transformá-lo em um async Taskpara que os chamadores possam aguardar e, em seguida, anexar Async.

Luke Puplett
fonte
20
Bem, é uma pena que não tenhamos verificações de tempo de compilação para chamadas de método ... oh, espere. Se eu nomear o método Get ou GetAsync e não use aguardam do lado chamando, a compilação irá falhar a construir. Portanto, essa convenção é BOA e realmente vai contra muitas diretrizes de estilo da Microsoft, como evitar coisas como PersonStringou PriceDecimalpor que usar GetAsync- os consumidores de APIs de APIs assíncronos não precisam se preocupar com isso, já que a solicitação sempre retorna após a conclusão de todas as tarefas. É bobo e realmente irritante para mim. Mas é apenas mais uma convenção que ninguém sabe realmente por que está lá.
Piotr Kula
2
@ppumkin: Como Stephen apontou, um método pode ser facilmente assíncrono por natureza sem usar async / await, portanto, o chamador não tem nenhuma indicação além do nome se a funcionalidade é executada ou não de forma assíncrona.
Hannobo
6
@ppumkin: Deixar de aguardar um método assíncrono, por padrão, resulta em um aviso em tempo de compilação; não é um erro de construção.
Dustin Cleveland
7
Acho essa convenção boba. Existem três indicações automáticas de que um método é assíncrono: 1. O tipo de retorno é Tarefa. 2. O preenchimento de código apresenta uma dica aguardável 3. o IDE irá avisá-lo sublinhando em verde e apresentando um aviso do compilador. Portanto, concordo totalmente com @ppumkin. O sufixo Async é tão bobo como se você escrevesse uma propriedade como: public Lazy <Customer> CustomerLazy. Quem faria isso! ??
Marco
2
@Marco Levantei essa ideia no GitHub, achei que era o melhor lugar, mas não tenho o engajamento que achei que conseguiria: github.com/dotnet/core/issues/1464
Luke Puplett
68

Eu construo muitos serviços de API e outros aplicativos que chamam outros sistemas onde a maior parte do meu código está rodando de forma assíncrona.

Minha própria regra prática que estou seguindo é:

Se houver métodos não assíncronos e assíncronos que retornem a mesma coisa, eu sufixo o assíncrono com Async. Caso contrário, não.

Exemplos:

Apenas um método:

public async Task<User> GetUser() { [...] }

Mesmo método com duas assinaturas:

public User GetUser() { [...] }

public async Task<User> GetUserAsync() { [...] }

Isso faz sentido, pois são os mesmos dados que são retornados, mas a única coisa que difere é a maneira de retornar os dados , não os dados em si.

Eu também acho que essas convenções de nomenclatura existem devido à necessidade de introduzir métodos assíncronos e ainda manter a compatibilidade com versões anteriores.

Eu defendo que o novo código não deve usar o sufixo Async. É tão óbvio quanto o tipo de retorno de String ou Int, conforme mencionado anteriormente neste tópico.

Jonas Stensved
fonte
8
Eu concordo, especialmente que normalmente você precisa usar 'assíncrono todo o caminho', caso em que o sufixo é redundante - qual é o sentido de anexá-lo a 90% do código;)
Bartosz
3
essa é a melhor solução. Sem perceber, tenho feito o mesmo com minhas APIs.
Marco
2
Isso é muito melhor do que sufixar com "Async" todos os métodos de aplicação assíncrona
Mariusz Jamro
O problema com essa técnica é que, se você criar uma versão não assíncrona posteriormente, não poderá usar seu nome "GetUser ()" de outra forma preferido.
David
3
Este é o caminho pragmático a seguir. Anexar Async a cada método que tem o modificador async é apenas Hungarian Notation 2019. @David se você acabar adicionando uma versão não assíncrona posteriormente, renomeie os métodos e siga a convenção de nomenclatura ou simplesmente não faça isso.
Skrymsli de
25

Qual é a convenção para sufixar nomes de métodos com "Async".

O padrão assíncrono baseado em tarefas (TAP) determina que os métodos devem sempre retornar um Task<T>(ou Task) e ser nomeados com um sufixo Async ; isso é separado do uso de async. Ambos Task<bool> Connect()e serão compilados e executados perfeitamente, mas você não seguirá a convenção de nomenclatura TAP.asyncTask<bool> Connect()

O método deve conter o asyncmodificador ou o suficiente para que ele retorne apenas Task?

Se o corpo do método (independentemente do tipo de retorno ou nome) incluir await, você deve usar async; e o compilador dirá "O operador 'await' só pode ser usado dentro de um método assíncrono. ...". Retornar Task<T>ou Tasknão é "suficiente" para evitar o usoasync . Consulte async (referência C #) para obter detalhes.

Ou seja, quais dessas assinaturas estão corretas:

Ambos e siga corretamente as convenções TAP. Você sempre pode usar a palavra - chave, mas obterá um aviso do compilador "Este método assíncrono não possui operadores 'await' e será executado de forma síncrona. ..." se o corpo não usar .asyncTask<bool> ConnectAsync()Task<bool> ConnectAsync()asyncawait

Ðаn
fonte
1
Ele está se referindo ao fato de você anexar ou não "Async" ao nome do método, e não ao uso da asyncpalavra - chave.
Servy
1
@Servy se ou não usar a palavra async- chave é a segunda parte da pergunta.
Corak de
2
@Servy É uma questão de duas partes. A primeira parte, como você disse, é se deve ou não acrescentar "Async" ao nome do método. A segunda parte é usar ou não o asyncmodificador. Veja também exemplos de OPs, public async Task<bool> ConnectAsync()(com asyncmodificador) vs public Task<bool> ConnectAsync()(sem asyncmodificador). O próprio nome do método tem o sufixo "Async" em ambos os casos.
Corak
2
É não uma questão em duas partes. A questão é se "Async" deve ser acrescentado aos nomes de métodos de métodos que retornam Taskou métodos que têm async Task.
kasperhj
3
@lejon: você deve melhorar a questão; o "vs." trechos de código deixam claro que a questão (em sua totalidade) é sobre assíncrono, pois essa é a única diferença.
Ðаn
11

ou o suficiente para apenas retornar Task?

Este. A asyncpalavra-chave não é o verdadeiro problema aqui. Se você implementar a assincronia sem usar a asyncpalavra - chave, o método ainda será "Async", no sentido geral.

Servy
fonte
7

Visto que Taske Task<T>são tipos aguardáveis, eles representam algumas operações assíncronas. Ou pelo menos eles deveriam representar.

Você deve adicionar sufixo Asynca um método que, em alguns casos (não necessariamente todos), não retorna um valor, mas sim um invólucro em torno de uma operação em andamento. Esse wrapper geralmente é um Task, mas no Windows RT pode ser IAsyncInfo. Siga sua intuição e lembre-se de que se um usuário de seu código vir a Asyncfunção, ele saberá que a invocação desse método está desacoplada do resultado desse método e que ele precisa agir de acordo.

Observe que existem métodos como Task.Delaye Task.WhenAllque retornam Taske ainda não têm o Asyncsufixo.

Observe também que existem async voidmétodos que representam disparar e esquecer o método assíncrono e é melhor você estar ciente de que o método é construído dessa forma.

Toni Petrina
fonte
6

Eu diria que ele deve usar o sufixo Async se retornar uma Task, independentemente se o método é declarado com o asyncmodificador ou não.

A razão por trás disso é que o nome é declarado na interface. A interface declara o tipo de retorno que é a Task. Então, há duas implementações dessa interface, uma implementação a implementa usando o asyncmodificador, a outra não.

public interface IFoo
{
    Task FooAsync();
}

public class FooA : IFoo
{
    public Task FooAsync() { /* ... */ }
}

public class FooB : IFoo
{
    public async Task FooAsync() { /* ... */ }
}
Fred
fonte
Isto é tão verdade. Sempre usamos interfaces, em todos os lugares, e as interfaces não podem ser declaradas assíncronas. Portanto, os guias oficiais sobre o uso do sufixo Async parecem completamente sem sentido para mim. Acho que a palavra-chave async é apenas um detalhe de implementação, parte da parte interna do método, e não deve influenciar seu nome ou qualquer outra coisa externa.
Al Kepp
5

Na programação assíncrona com async e await (C #) , a Microsoft oferece a seguinte orientação:

Convenção de nomes

Por convenção, você anexa "Async" aos nomes dos métodos que possuem um modificador async .

Você pode ignorar a convenção em que um evento, classe base ou contrato de interface sugere um nome diferente. Por exemplo, você não deve renomear manipuladores de eventos comuns, comoButton1_Click .

Acho esta orientação incompleta e insatisfatória. Isso significa que, na ausência do asyncmodificador, esse método deve ser nomeado em Connectvez de ConnectAsync?

public Task<bool> ConnectAsync()
{
    return ConnectAsyncInternal();
}

Acho que não. Como indicado na resposta concisa por @Servy e quanto mais resposta detalhada por @Luke Puplett , eu acredito que é apropriado e, na verdade esperava que este método deve ser nomeado ConnectAsync(porque ele retorna um awaitable). Em mais apoio deste, @ John Skeet em esta resposta a outra pergunta anexa Asyncao nome do método independentemente da presença doasync modificador.

Finalmente, em outra questão , considere este comentário de @Damien_The_Unbeliever :

async/awaitsão detalhes de implementação de seus métodos. Não importa nem um pouco se o seu método é declarado async Task Method()ou apenas Task Method(), na medida em que seus chamadores . (Na verdade, você é livre para alternar entre esses dois posteriormente, sem que isso seja considerado uma alteração significativa.)

A partir disso, deduzo que é a natureza assíncrona do método que determina como ele deve ser nomeado. O usuário do método nem saberá se o asyncmodificador é usado em sua implementação (sem o código-fonte C # ou CIL).

DavidRR
fonte