¿Existe una convención de nomenclatura definitiva para los métodos que devuelven IAsyncEnumerable?

10

Después de que C # 5 presentó el modelo asyncy awaitpara la programación asincrónica, la comunidad C # llegó a una convención de nomenclatura para agregar un sufijo "Async" a los métodos que devuelven un tipo esperado, como este:

interface Foo
{
   Task BarAsync();
}

Desde entonces, se ha escrito que muchos analizadores de código estático (tanto basados ​​en Roslyn como no basados ​​en Roslyn) dependen de esta convención de nomenclatura al detectar el olor del código en torno a la programación asincrónica.

Ahora que C # 8 ha introducido el concepto de enumerables asincrónicos, que no son esperados pero que pueden usarse en conjunto await foreach, parece que hay dos opciones para devolver los métodos de nombres IAsyncEnumerable:

interface Foo
{
    // No "Async" suffix, indicating that the return value is not awaitable.
    IAsyncEnumerable<T> Bar<T>();
}

o

interface Foo
{
    // With "Async" suffix, indicating that the overall logic is asynchronous.
    IAsyncEnumerable<T> BarAsync<T>();
}

¿Ha habido una directriz definitiva para la convención de nomenclatura (del equipo del lenguaje C #, la Fundación .NET u otras autoridades) con respecto a las opciones anteriores, como cómo la convención de nomenclatura C # 5 fue estandarizada sin ambigüedades y no se dejó al criterio de los programadores basado en la opinión?

hwaien
fuente
2
La opción 2 suena correcta aquí, ya que ejecuta este código de forma asincrónica (marque el método como asyncy use awaitdentro), y el ejemplo msdn muestra lo mismo
Pavel Anikhouski el
1
En respuesta al cierre de la pregunta, he editado la pregunta para preguntar si existe una convención de nomenclatura definitiva y para dar un ejemplo de cómo una convención de nomenclatura definitiva en C # 5 resultó en desarrollos en el análisis de código estático. Por lo tanto, hacer que C # 8 siga ese patrón dará como resultado mejoras cuantificables en la calidad del código más allá de las preferencias de codificación basadas en la opinión.
hwaien
.NET Core usa el Asyncsufijo para los métodos que regresan IAsyncEnumerable, por ejemplo, ChannelReader.ReadAllAsync . En otros casos, por ejemplo, en EF Core, AsAsyncEnumerable()se usa lo que ya está claro
Panagiotis Kanavos
Existen pautas de nomenclatura para facilitar que los humanos comprendan el código. Si el propósito del código no está claro, agregue el sufijo.
Panagiotis Kanavos

Respuestas:

1

No hay mejor directriz que lo que los equipos .NET ya hacen:

  • ChannelReader.ReadAllAsync devuelve unIAsyncEnumerable<T>
  • En EF Core 3, los resultados se devuelven como IAsyncEnumerablellamando a AsAsyncEnumerable ()
  • En System.Linq.Async, ToAsyncEnumerable () convierte IEnumerables, Tareas y Observables en IAsyncEnumerables
  • Todos los demás operadores en System.Linq.Asyncconservan sus nombres. No hay SelectAsynco SelectAsAsyncEnumerable, simplemente Select.

En todos los casos, está claro cuál es el resultado del método. En todos los casos, los resultados del método deben esperarse await foreachantes de que puedan usarse.

Por lo tanto, la pauta real sigue siendo la misma: asegúrese de que el nombre aclare el comportamiento :

  • Cuando el nombre ya está claro, por ejemplo, con AsAsyncEnumerable()o ToAsyncEnumerable(), no es necesario agregar ningún sufijo.
  • En otros casos, agregue el Asyncsufijo para que los desarrolladores sepan que necesitan await foreachel resultado.

Los analizadores y generadores de código no se preocupan realmente por los nombres de los métodos, detectan olores al inspeccionar el código en sí. Un analizador de código le dirá que ha olvidado a la espera de una tarea o await foreachun IAsyncEnumerableno importa cómo se llama a los métodos y las variables. Un generador puede simplemente usar la reflexión para verificar IAsyncEnumerabley emitirawait foreach

Son los analizadores de estilo los que verifican los nombres. Su trabajo es garantizar que el código use un estilo consistente para que los desarrolladores puedan entender el código. El analizador de estilo le dirá que un método no sigue el estilo que eligió. Ese estilo puede ser el del equipo o una guía de estilo comúnmente aceptada.

Y, por supuesto, todos saben que el prefijo común para los campos de instancia privada es _:)

Panagiotis Kanavos
fuente
Gracias por señalar el ChannelReader.ReadAllAsyncejemplo. Dado que esto proviene directamente del equipo .NET, es difícil equivocarse siguiendo el ejemplo.
hwaien
Los paquetes de analizadores a menudo vienen con una mezcla de analizadores con estilo y sin estilo. Por ejemplo, el paquete Microsoft.VisualStudio.Threading.Analyzers tiene un analizador de estilo que verifica la convención de nomenclatura (VSTHRD200) y un analizador sin estilo que detecta situaciones peligrosas que pueden conducir a un punto muerto (VSTHRD111). Para hacer referencia al paquete para aprovechar VSTHRD111, un proyecto también terminaría con VSTHRD200.
hwaien
2

No es un método asíncrono, por lo que el nombre no debe terminar en 'Asíncrono'. Ese sufijo de método es una convención para hacer obvio que el método debe esperarse o que el resultado debe manejarse como una Tarea.

Creo que un nombre normal de devolución de colección es apropiado. GetFoos (), o similar.

David Browne - Microsoft
fuente
Todos esos son argumentos para usar el Asyncsufijo. El sufijo se usa en .NET Core: ChannelReader.ReadAllAsync devuelve un IAsyncEnumerable. Se IAsyncEnumerabledebe usar An await foreach, por lo que el sufijo tiene sentido.
Panagiotis Kanavos
1

El sufijo asíncrono e incluso la palabra clave asynces simplemente repetitivo, no tiene sentido aparte de algunos argumentos de compatibilidad con versiones anteriores. C # tiene toda la información para distinguir esas funciones al igual que se distingue yielden los métodos que se devuelven IEnumerable, ya que sabe que no necesita agregar nada parecido enumerableo alguna otra palabra clave en dichos métodos.

Debe agregar el sufijo Async solo porque C # se quejará de sobrecargas, por lo que esencialmente esos dos son idénticos y hacen lo mismo según todas las reglas del polimorfismo (si no tomamos en cuenta el factor humano de comportamiento de corrupción intencional):

public interface IMyContract
{
    Task<int> Add(int a, int b);
    int Add(int a, int b);
}

Pero no puedes escribirlo así porque el compilador se quejará. ¡Pero hey! Se tragará esta monstruosidad:

public interface IMyContract : IEnumerable<int>, IEnumerable<long>
{
}

e incluso esto:

public interface IMyContractAsync
{
    Task<int> Add(int a, int b);
}

public interface IMyContract : IMyContractAsync
{
    int Add(int a, int b);
}

Así que esto es todo. Agrega Async solo para detener el compilador para quejarse. No para aclarar las cosas. No para hacerlos mejores. Si no hay ninguna queja, no hay razón para agregarla, así que en su caso evitaré esto, a menos que se convierta Task<IAsyncEnumerable>.

PD: Solo para comprender mejor la imagen completa aquí: si en algún momento en el futuro alguien agrega extensiones de método de computadora cuántica C # (fantazy, huh) que operarán en un paradigma diferente, probablemente necesitemos otro sufijo como Quant o algo así, porque C # se quejará de lo contrario. Será exactamente el mismo método, pero funcionará de otra manera. Al igual que el polimorfismo. Al igual que las interfaces.

eocron
fuente
1
Lo siento, pero siento que no estoy de acuerdo, no es un enfoque realmente diferente de hacer lo mismo, es más, se espera que se llame de manera diferente, y que el resultado se recopile de manera diferente. Esa es la mayor diferencia entre async y no async. Creo firmemente que agregar el asíncrono es para mayor claridad. Creo que esta también es la recomendación de Microsoft.
madoxdev
Sí, estoy de acuerdo pero no estoy de acuerdo. Al igual que en la Lista, tiene una función de clasificación simple y no "QuickSort", "RadixSort", "BubbleSort", "MixOfSorts". Son diferentes, se usan por alcance pero obviamente no necesitan aclaraciones que no sean para fines de depuración (quiero decir que solo se preocupan por ellos cuando tienen un impacto en el rendimiento / recursos). En este caso, DoThing y DoThingAsync se encuentran en la misma situación que cualquier otro algoritmo con plantilla, no necesitan aclaraciones, son compatibles o no, y solo son útiles para la depuración.
eocron
Eso es diferente, significa asíncrono: la persona que llama se asegura de manejarlo correctamente. El hecho de retirning Task no es suficiente para decir que el método es realmente Async. La persona que llama debe saber esperar o usar GetAwaiter (). GetResilt () para obtener la misma salida. Eso es diferente, no cómo se hacen las cosas por dentro.
madoxdev
Este es completamente el propósito de los analizadores de código y el compilador. IntellySense puede señalar esto fácilmente para usted, no es necesario utilizar los cerebros de los desarrolladores para resaltar o incluso desterrar el uso del método de sincronización sobre uno asíncrono en código asíncrono. Según mi experiencia al usar Async, rara vez necesito usar GetResult (), pero con frecuencia necesito contaminar toda la base de código con Async.
eocron
Como alguien marcó eso, es opinión. Podemos seguir creyendo en lo que creemos y ser felices :)
madoxdev