Utwórz pusty IAsyncEnumerable

25

Mam interfejs napisany w ten sposób:

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

Chcę napisać pustą implementację, która nie zwraca żadnego elementu, na przykład:

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

Gdyby to był zwykły IEnumerable, zrobiłbym to return Enumerable.Empty<string>();, ale nie znalazłem żadnego AsyncEnumerable.Empty<string>().

Obejścia

Znalazłem to, które działa, ale jest dość dziwne:

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

Dowolny pomysł?

cube45
źródło

Odpowiedzi:

28

Jeśli zainstalujesz System.Linq.Asyncpakiet, powinieneś być w stanie korzystać AsyncEnumable.Empty<string>(). Oto kompletny przykład:

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
źródło
Dziękujemy za szybką odpowiedź i sugestię. Miałem nadzieję, że coś istnieje w ramach.
cube45
@ cube45: Ogólnie uważałbym System.Linq.Asyncza „praktycznie część frameworka”. Jest bardzo niewiele, co tylko w standardzie netto 2.1 IAsyncEnumerable<T>.
Jon Skeet,
@ cube45 Byłbym ostrożny, aby nie używać pakietu. Jest wiele kwarków z przepływami asynchronicznymi, które odkryjesz, gdy zaczniesz z niego korzystać, chyba że naprawdę wiesz, co robisz.
Filip Cordas
Dziękuję za odpowiedzi. Nigdy wcześniej nie korzystałem z IAsyncEnumerable i po prostu eksperymentowałem, nie robiąc czegoś „na serio”. Prawdopodobnie masz rację, pakiet może być przydatny.
cube45
Istnieje problem, jeśli jest używany z efcore github.com/dotnet/efcore/issues/18124
Pavel Shastov
11

Jeśli z jakiegoś powodu nie chcesz instalować pakietu wymienionego w odpowiedzi Jona, możesz utworzyć następującą metodę AsyncEnumerable.Empty<T>():

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);
    }
}

Uwaga: odpowiedź nie zniechęca do korzystania z System.Linq.Asyncpakietu. Ta odpowiedź zawiera krótką implementację AsyncEnumerable.Empty<T>()potrzebnych przypadków i nie możesz / nie chcesz korzystać z pakietu. Implementację zastosowaną w pakiecie można znaleźć tutaj .

Reza Aghaei
źródło
Dzięki za odpowiedź. Rzeczywiście, to też byłaby opcja. Myślę, że wolałbym w ten sposób, niż instalować inny pakiet. Oznaczę ten jako przyjęty. Nitpick: Mówisz „metoda rozszerzenia”, podczas gdy jest to tylko metoda statyczna w klasie statycznej.
cube45
1
@ cube45: Więc nie planujesz używać żadnej funkcji LINQ z zaangażowanymi sekwencjami asynchronicznymi? Ponieważ gdy tylko chcesz zrobić cokolwiek, co normalnie zrobiłbyś z synchronicznym LINQ, potrzebujesz System.Linq.Async.
Jon Skeet,