¿El uso del sufijo "Async" en el nombre de un método depende de si se usa el modificador "async"?

106

¿Cuál es la convención para agregar sufijos a los nombres de métodos con "Async"?

¿Debería añadirse el sufijo "Async" sólo a un método que se declara con el asyncmodificador?

public async Task<bool> ConnectAsync()

¿O es suficiente que el método simplemente regrese Task<T>o Task?

public Task<bool> ConnectAsync()
Kasperhj
fuente
4
Para la parte de nombres, el documento TAP dice: Los métodos asincrónicos en TAP incluyen el sufijo Async después del nombre de la operación; por ejemplo, GetAsync para una operación de obtención. Si está agregando un método TAP a una clase que ya contiene ese nombre de método con el sufijo Async, use el sufijo TaskAsync en su lugar. Por ejemplo, si la clase ya tiene un método GetAsync, use el nombre GetTaskAsync.
James Manning
4
ok, supongo que estaba confundido por el título de la pregunta de "Convención de nomenclatura para métodos asincrónicos"
James Manning
1
Ésta es una pregunta mal construida. Gente discutiendo, respuestas equívocas.
Luke Puplett
4
Porque muchas personas lo han entendido mal y están discutiendo sobre lo que realmente se está preguntando, preguntándose si es una pregunta de dos partes, etc. La prueba de que es confuso es que la gente está confundida.
Luke Puplett
2
@DavidRR Hasta el día de hoy todavía no entiendo la cantidad de confusión que aparentemente ha causado esta pregunta. Si sus ediciones traen algo de orden en la confusión de modo que le haya ayudado y posiblemente pueda ayudar a otros, entonces agradezco sus ediciones porque ha logrado algo que yo no pude en la formulación original. La pregunta ahora es tan antigua que apenas puedo recordar mi forma de pensar cuando la hice aquí, por lo que la intención original es menos importante. La respuesta de Luke refleja que no todos estaban confundidos. Lo encontré inmensamente útil.
kasperhj

Respuestas:

127

Creo que la verdad es ambigua incluso en la documentación de Microsoft:

En Visual Studio 2012 y .NET Framework 4.5, cualquier método que se atribuya con la asyncpalabra clave ( Asyncen Visual Basic) se considera un método asincrónico, y los compiladores de C # y Visual Basic realizan las transformaciones necesarias para implementar el método de forma asincrónica mediante TAP. Un método asincrónico debe devolver Taskun Task<TResult>objeto o un objeto.

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

Eso ya no está bien. Cualquier método con asynces asincrónico y luego dice que debe devolver un Tasko Task<T>, lo que no es correcto para los métodos en la parte superior de una pila de llamadas, Button_Click por ejemplo, o async void.

Por supuesto, hay que considerar cuál es el objetivo de la convención.

Se podría decir que la Asyncconvención de sufijos es comunicar al usuario de la API que el método está disponible. Para que un método sea de espera, debe regresar Taskpara un Task<T>método vacío o para un método de devolución de valor, lo que significa que solo el último puede tener el sufijo Async.

O podría decir que la Asyncconvención de sufijos es comunicar que el método puede regresar inmediatamente, abandonando el hilo actual para realizar otro trabajo y potencialmente causando carreras.

Esta cita de Microsoft doc dice:

Por convención, agrega "Async" a los nombres de los métodos que tienen un modificador Async o async.

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

Lo cual ni siquiera menciona que sus propios métodos asincrónicos que regresan Tasknecesitan el Asyncsufijo, que creo que todos estamos de acuerdo en que lo hacen.


Entonces, la respuesta a esta pregunta podría ser: ambos. En ambos casos, debe agregar Asyncmétodos con la asyncpalabra clave y que devuelvan Tasko Task<T>.


Le voy a pedir a Stephen Toub que aclare la situación.

Actualizar

Así que lo hice. Y esto es lo que escribió nuestro buen hombre:

Si un método público devuelve una tarea y es de naturaleza asíncrona (a diferencia de un método que se sabe que siempre se ejecuta de forma síncrona hasta su finalización, pero que aún devuelve una tarea por alguna razón), debe tener un sufijo "Asíncrono". Esa es la pauta. El objetivo principal aquí con la denominación es hacer que sea muy obvio para un consumidor de la funcionalidad que el método que se invoca probablemente no completará todo su trabajo de forma sincrónica; por supuesto, también ayuda con el caso en el que la funcionalidad se expone con métodos sincrónicos y asincrónicos, de modo que necesita una diferencia de nombre para distinguirlos. La forma en que el método logra su implementación asincrónica es irrelevante para la denominación: si async / await se usa para obtener la ayuda del compilador, o si los tipos y métodos de System.Threading.Tasks se usan directamente (p. Ej. gramo. TaskCompletionSource) realmente no importa, ya que eso no afecta la firma del método en lo que respecta al consumidor del método.

Por supuesto, siempre hay excepciones a una guía. El más notable en el caso de la asignación de nombres serían los casos en los que la razón de ser de un tipo completo es proporcionar una funcionalidad centrada en async, en cuyo caso tener Async en cada método sería excesivo, por ejemplo, los métodos en Task en sí que producen otras Tasks. .

En cuanto a los métodos asincrónicos de devolución de vacío, no es deseable tenerlos en un área de superficie pública, ya que la persona que llama no tiene una buena forma de saber cuándo se ha completado el trabajo asincrónico. Sin embargo, si debe exponer públicamente un método asincrónico de retorno vacío, es probable que desee tener un nombre que transmita que se está iniciando el trabajo asincrónico, y podría usar el sufijo "Async" aquí si tiene sentido. Dado lo raro que debería ser este caso, diría que en realidad es una decisión caso por caso.

Espero que eso ayude, Steve

La guía sucinta de la oración inicial de Stephen es bastante clara. Excluye async voidporque es inusual querer crear una API pública con tal diseño, ya que la forma correcta de implementar un vacío asincrónico es devolver una Taskinstancia simple y dejar que el compilador haga su magia. Sin embargo, si desea un public async void, Asyncse recomienda agregarlo . Otros async voidmétodos de primer nivel, como los controladores de eventos, generalmente no son públicos y no importan / califican.

Para mí, me dice que si me pregunto sobre cómo agregar Asyncun sufijo a un async void, probablemente debería convertirlo en un async Taskpara que las personas que llaman puedan esperarlo y luego agregarlo Async.

Luke Puplett
fuente
20
Bueno, es una pena que no tengamos comprobaciones de tiempo de compilación para llamadas a métodos ... oh, espera. Si nombro el Método Obtener o GetAsync y no uso aguardan desde el lado que llama, la compilación fallará a construir. Entonces, esta convención es TONTA y realmente va en contra de muchas pautas de estilo de Microsoft, como evitar cosas como PersonStringo PriceDecimalasí por qué usar GetAsync: los consumidores de API de API asíncronas no deben preocuparse por esto, ya que la solicitud siempre regresa después de que todas las tareas se completan de todos modos. Es una tontería y realmente me molesta. Pero es solo otra convención que nadie sabe realmente por qué está ahí.
Piotr Kula
2
@ppumkin: como señaló Stephen, un método puede ser fácilmente de naturaleza asincrónica sin usar async / await, por lo que la persona que llama no tiene ninguna indicación más que el nombre de si la funcionalidad se ejecuta de forma asincrónica o no.
Hannobo
6
@ppumkin: no esperar un método asíncrono, de forma predeterminada, da como resultado una advertencia en tiempo de compilación; no es un error de construcción.
Dustin Cleveland
7
Encuentro esta convención tonta. Hay tres indicaciones automáticas de que un método es asíncrono: 1. El tipo de retorno es Tarea. 2. La finalización del código presenta una pista que se espera. 3. El IDE le advertirá subrayando en verde y presentando una advertencia del compilador. Así que estoy totalmente de acuerdo con @ppumkin. El sufijo Async es tan tonto como si escribiera una propiedad como esta: public Lazy <Customer> CustomerLazy. ¿Quién haría esto?
Marco
2
@Marco Planteé esta idea en GitHub, pensé que era el mejor lugar, pero no tengo el compromiso que pensé que obtendría: github.com/dotnet/core/issues/1464
Luke Puplett
68

Construyo muchos servicios de API y otras aplicaciones que llaman a otros sistemas donde la mayor parte de mi código se ejecuta de forma asincrónica.

Mi propia regla general que estoy siguiendo es:

Si hay un método no asíncrono y asíncrono que devuelve lo mismo, le agrego el sufijo asíncrono con Asíncrono. De otra forma no.

Ejemplos:

Solo un método:

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

Mismo método con dos firmas:

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

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

Esto tiene sentido ya que se devuelven los mismos datos, pero lo único que difiere es la forma de devolver los datos , no los datos en sí.

También creo que estas convenciones de nomenclatura existen debido a la necesidad de introducir métodos asincrónicos y aún mantener la compatibilidad con versiones anteriores.

Sostengo que el nuevo código no debería usar el sufijo Async. Es tan obvio como el tipo de retorno de String o Int como se mencionó anteriormente en este hilo.

Jonas Stensved
fuente
8
Estoy de acuerdo, especialmente en que, por lo general, debe ir 'asíncrono hasta el final', en cuyo caso el sufijo es redundante: ¿cuál es el punto de agregarlo al 90% del código;)
Bartosz
3
Esta es la mejor solución. Sin darme cuenta, he estado haciendo lo mismo en mis API.
Marco
2
Esto es mucho mejor que agregar el sufijo "Async" en todos los métodos de aplicación async
Mariusz Jamro
El problema con esta técnica es que si crea una versión no asincrónica más adelante, no puede usar su nombre "GetUser ()" preferido.
David
3
Este es el camino pragmático a seguir. Agregar Async a cada método que tiene el modificador async es solo la notación húngara 2019. @David, si terminas agregando una versión no asincrónica más tarde, cambia el nombre de los métodos y sigue la convención de nomenclatura o simplemente no lo hagas.
Skrymsli
25

¿Cuál es la convención para agregar sufijos a los nombres de métodos con "Async"?

El patrón asincrónico basado en tareas (TAP) dicta que los métodos siempre deben devolver un Task<T>(o Task) y ser nombrados con un sufijo Async ; esto es independiente del uso de async. Ambos Task<bool> Connect()y se compilarán y ejecutarán bien, pero no seguirás la convención de nomenclatura TAP.asyncTask<bool> Connect()

¿Debería el método contener el asyncmodificador, o lo suficiente como para devolver Task?

Si el cuerpo del método (independientemente del tipo de retorno o el nombre) incluye await, debe usar async; y el compilador le dirá "El operador 'await' solo se puede usar dentro de un método asincrónico. ...". Volver Task<T>o Taskno es "suficiente" para evitar su uso async. Consulte async (Referencia de C #) para obtener más detalles.

Es decir, cuáles de estas firmas son correctas:

Ambos y siguen correctamente las convenciones de TAP. Usted podría siempre utilizar la palabra clave, pero vas a obtener una advertencia del compilador "Este método asíncrono carece '' aguardan los operadores y se ejecutará de forma sincrónica. ..." si el cuerpo no utiliza .asyncTask<bool> ConnectAsync()Task<bool> ConnectAsync()asyncawait

Un
fuente
1
Se refiere a si agrega o no "Async" al nombre del método, no a si usa la asyncpalabra clave.
Servicio
1
@Servy, ya sea que use o no la palabra clave asynces la segunda parte de la pregunta.
Corak
2
@Servy Es una pregunta de dos partes. La primera parte, como dijiste, es si agregar o no "Async" al nombre del método. La segunda parte es si usar o no el asyncmodificador. Consulte también los ejemplos de OP, public async Task<bool> ConnectAsync()(con asyncmodificador) vs public Task<bool> ConnectAsync()(sin asyncmodificador). El nombre del método en sí tiene el sufijo "Async" en ambos casos.
Corak
2
Es no una pregunta de dos partes. La pregunta es si se debe agregar "Async" a los nombres de métodos de los métodos que devuelven Tasko los métodos que tienen async Task.
kasperhj
3
@lejon: deberías mejorar la pregunta; el "vs." Los fragmentos de código dejan en claro que la pregunta (en su totalidad) es sobre async, ya que esa es la única diferencia.
2013
11

¿O es suficiente que solo devuelva Task?

Ese. La asyncpalabra clave no es el problema real aquí. Si implementa la asincronía sin usar la asyncpalabra clave, el método sigue siendo "Async", en el sentido general.

Servy
fuente
7

Dado que Tasky Task<T>son tipos de espera, representan una operación asincrónica. O al menos deberían representar.

Debe agregar un sufijo Asynca un método que, en algunos casos (no necesariamente todos), no devuelve un valor, sino que devuelve un contenedor alrededor de una operación en curso. Ese contenedor suele ser un Task, pero en Windows RT puede serlo IAsyncInfo. Siga su instinto y recuerde que si un usuario de su código ve la Asyncfunción, sabrá que la invocación de ese método está desacoplada del resultado de ese método y que debe actuar en consecuencia.

Tenga en cuenta que hay métodos como Task.Delayy Task.WhenAllque regresan Tasky, sin embargo, no tienen el Asyncsufijo.

También tenga en cuenta que hay async voidmétodos que representan fuego y olvidan el método asincrónico y es mejor que tenga en cuenta que el método está construido de esa manera.

Toni Petrina
fuente
6

Yo diría que debería usar el sufijo Async si devuelve una tarea independientemente de si el método se declara con el asyncmodificador o no.

La razón detrás de esto es que el nombre se declara en la interfaz. La interfaz declara el tipo de retorno que es a Task. Luego hay dos implementaciones de esa interfaz, una implementación la implementa usando el asyncmodificador, la otra no.

public interface IFoo
{
    Task FooAsync();
}

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

public class FooB : IFoo
{
    public async Task FooAsync() { /* ... */ }
}
Fred
fuente
Esto es tan cierto. Siempre usamos interfaces, en todas partes, y las interfaces no se pueden declarar asíncronas. Entonces, las guías oficiales sobre el uso del sufijo Async me parecen completamente absurdas. Creo que la palabra clave async es solo un detalle de implementación, parte de los aspectos internos del método, y no debería influir en su nombre ni en nada externo.
Al Kepp
5

En la programación asincrónica con async y await (C #) , Microsoft ofrece la siguiente guía:

Convenio de denominación

Por convención, agrega "Async" a los nombres de los métodos que tienen un modificador async .

Puede ignorar la convención donde un evento, clase base o contrato de interfaz sugiere un nombre diferente. Por ejemplo, no debe cambiar el nombre de los controladores de eventos comunes, como Button1_Click.

Encuentro esta guía incompleta e insatisfactoria. ¿Significa esto que, en ausencia del asyncmodificador, este método debería nombrarse en Connectlugar de ConnectAsync?

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

No lo creo. Como se indica en la respuesta concisa de @Servy y la respuesta más detallada de @Luke Puplett , creo que es apropiado y, de hecho, se espera que este método deba ser nombrado ConnectAsync(porque devuelve un awaitable). En mayor apoyo a esto, @John Skeet en esta respuesta a otra pregunta se agrega Asyncal nombre del método independientemente de la presencia del asyncmodificador.

Finalmente, en otra pregunta , considere este comentario de @Damien_The_Unbeliever :

async/awaitson detalles de implementación de sus métodos. No importa un ápice si su método está declarado async Task Method()o simplemente Task Method(), en lo que respecta a las personas que llaman . (De hecho, puede cambiar entre estos dos en un momento posterior sin que se considere un cambio importante).

De eso, infiero que es la naturaleza asincrónica del método lo que dicta cómo debe nombrarse. El usuario del método ni siquiera sabrá si el asyncmodificador se usa en su implementación (sin el código fuente de C # o CIL).

DavidRR
fuente