Criar IAsyncEnumerable vazio

25

Eu tenho uma interface que é escrita assim:

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

Eu quero escrever uma implementação vazia que não retorna nenhum item, assim:

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

Se fosse um IEnumerable simples, eu o faria return Enumerable.Empty<string>();, mas não encontrei nenhum AsyncEnumerable.Empty<string>().

Soluções alternativas

Achei isso que funciona, mas é bastante estranho:

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

Qualquer ideia?

cube45
fonte

Respostas:

28

Se você instalar o System.Linq.Asyncpacote, poderá usar AsyncEnumable.Empty<string>(). Aqui está um exemplo 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
fonte
Obrigado pela sua pronta resposta e pela sugestão. Eu esperava que algo existisse na estrutura.
precisa saber é
@ cube45: Eu geralmente consideraria System.Linq.Async"virtualmente parte da estrutura". Há muito pouco que é apenas em netstandard2.1 quando se trata de IAsyncEnumerable<T>.
Jon Skeet
@ cube45 Gostaria de ter cuidado ao não usar o pacote. Existem muitos quarks com fluxos assíncronos que você descobrirá quando começar a usá-lo mais, a menos que saiba realmente o que está fazendo.
Filip Cordas
Obrigado por suas respostas. Eu nunca usei o IAsyncEnumerable antes e estava apenas experimentando, não fazendo algo "de verdade". Você provavelmente está certo, o pacote pode ser útil.
cube45
Existe um problema se ele estiver sendo usado com efcore github.com/dotnet/efcore/issues/18124
Pavel Shastov
11

Se, por algum motivo, você não quiser instalar o pacote mencionado na resposta de Jon, poderá criar o método da AsyncEnumerable.Empty<T>()seguinte maneira:

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: A resposta não desencoraja o uso do System.Linq.Asyncpacote. Esta resposta fornece uma breve implementação dos AsyncEnumerable.Empty<T>()casos que você precisa e não pode / não deseja usar o pacote. Você pode encontrar a implementação usada no pacote aqui .

Reza Aghaei
fonte
Obrigado pela sua resposta. De fato, isso seria uma opção também. Eu acho que prefiro dessa maneira do que instalar outro pacote. Vou marcar este como aceito. Nitpick: Você diz "método de extensão" enquanto é apenas um método estático em uma classe estática.
cube45
11
@ cube45: Então você não planeja usar nenhuma funcionalidade LINQ com as sequências assíncronas envolvidas? Como assim que você deseja fazer qualquer coisa que normalmente faria com o LINQ síncrono, precisará do System.Linq.Async.
Jon Skeet