Crear IAsyncEnumerable vacío

25

Tengo una interfaz que se escribe así:

public interface IItemRetriever
{
    public IAsyncEnumerable<string> GetItemsAsync();
}

Quiero escribir una implementación vacía que no devuelva ningún elemento, así:

public class EmptyItemRetriever : IItemRetriever
{
    public IAsyncEnumerable<string> GetItemsAsync()
    {
       // What do I put here if nothing is to be done?
    }
}

Si fuera un IEnumerable simple, lo haría return Enumerable.Empty<string>();, pero no encontré ninguno AsyncEnumerable.Empty<string>().

Soluciones alternativas

Encontré esto que funciona pero es bastante extraño:

public async IAsyncEnumerable<string> GetItemsAsync()
{
    await Task.CompletedTask;
    yield break;
}

¿Alguna idea?

cubo45
fuente

Respuestas:

28

Si instala el System.Linq.Asyncpaquete, debería poder usarlo AsyncEnumable.Empty<string>(). Aquí hay un ejemplo completo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        IAsyncEnumerable<string> empty = AsyncEnumerable.Empty<string>();
        var count = await empty.CountAsync();
        Console.WriteLine(count); // Prints 0
    }
}
Jon Skeet
fuente
Gracias por su pronta respuesta y la sugerencia. Esperaba que algo existiera en el marco.
cubo45
@ cube45: generalmente lo consideraría System.Linq.Asynccomo "prácticamente parte del marco". Hay muy poco lo que es justo en netstandard2.1 cuando se trata de IAsyncEnumerable<T>.
Jon Skeet
@ cube45 Tendría cuidado de no usar el paquete, hay muchos quarks con flujos asíncronos que descubrirá cuando comience a usarlo más, a menos que realmente sepa lo que está haciendo, realmente lo haría en la pepita.
Filip Cordas
Gracias por tus respuestas. Nunca usé IAsyncEnumerable antes, y solo estaba experimentando, no haciendo algo "de verdad". Probablemente tengas razón, el paquete puede ser útil.
cube45
Hay un problema si se utiliza con efcore github.com/dotnet/efcore/issues/18124
Pavel Shastov
11

Si por alguna razón no desea instalar el paquete que se menciona en la respuesta de Jon, puede crear el método de AsyncEnumerable.Empty<T>()esta manera:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public static class AsyncEnumerable
{
    public static IAsyncEnumerator<T> Empty<T>() => EmptyAsyncEnumerator<T>.Instance;

    class EmptyAsyncEnumerator<T> : IAsyncEnumerator<T>
    {
        public static readonly EmptyAsyncEnumerator<T> Instance = 
            new EmptyAsyncEnumerator<T>();
        public T Current => default!;
        public ValueTask DisposeAsync() => default;
        public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(false);
    }
}

Nota: La respuesta no desalienta el uso del System.Linq.Asyncpaquete. Esta respuesta proporciona una breve implementación de los AsyncEnumerable.Empty<T>()casos en los que lo necesita y no puede / no quiere usar el paquete. Puede encontrar la implementación utilizada en el paquete aquí .

Reza Aghaei
fuente
Gracias por tu respuesta. De hecho, esa también sería una opción. Creo que preferiría eso en lugar de instalar otro paquete. Marcaré este como aceptado. Nitpick: Dices "método de extensión" mientras que es solo un método estático en una clase estática.
cube45
1
@ cube45: Entonces, ¿no planea usar ninguna funcionalidad LINQ con las secuencias asincrónicas involucradas? Porque tan pronto como desee hacer algo que normalmente haría con LINQ síncrono, necesitará System.Linq.Async.
Jon Skeet