Создать пустой IAsyncEnumerable

25

У меня есть интерфейс, который написан так:

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

Я хочу написать пустую реализацию, которая не возвращает элемент, например, так:

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

Если бы это был простой IEnumerable, я бы return Enumerable.Empty<string>();это сделал, но я не нашел ни одного AsyncEnumerable.Empty<string>().

обходные

Я нашел это, которое работает, но довольно странно:

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

Есть идеи?

cube45
источник

Ответы:

28

Если вы установите System.Linq.Asyncпакет, вы сможете использовать его AsyncEnumable.Empty<string>(). Вот полный пример:

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
    }
}
Джон Скит
источник
Спасибо за ваш быстрый ответ и предложение. Я надеялся, что что-то существует в рамках.
cube45
@ cube45: я бы вообще расценил System.Linq.Asyncкак "фактически часть фреймворка". В netstandard2.1 очень мало всего, что касается IAsyncEnumerable<T>.
Джон Скит
@ cube45 Я бы не стал использовать пакет. Существует много кварков с асинхронными потоками, которые вы обнаружите, когда начнете использовать его чаще, если только вы действительно не будете знать, что делаете, я бы действительно использовал самородок.
Филип Кордас
Спасибо за ваши ответы. Я никогда не использовал IAsyncEnumerable прежде, и я просто экспериментировал, не делая что-то «по-настоящему». Вы, вероятно, правы, пакет может быть полезен.
cube45
Существует проблема, если она используется с efcore github.com/dotnet/efcore/issues/18124
Павел Шастов
11

Если по какой-либо причине вы не хотите устанавливать пакет, упомянутый в ответе Джона, вы можете создать метод 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);
    }
}

Примечание: ответ не препятствует использованию System.Linq.Asyncпакета. Этот ответ дает краткую реализацию AsyncEnumerable.Empty<T>()для случаев, когда он вам нужен, и вы не можете / не хотите использовать пакет. Вы можете найти реализацию, использованную в пакете, здесь .

Реза Агаи
источник
Спасибо за Ваш ответ. Действительно, это тоже будет вариант. Я думаю, что я бы предпочел этот способ, а не устанавливать другой пакет. Я отмечу этот как принятый. Nitpick: Вы говорите «метод расширения», а это просто статический метод в статическом классе.
cube45
1
@ cube45: Так вы не планируете использовать какие-либо функции LINQ с задействованными асинхронными последовательностями? Потому что, как только вы захотите сделать что-то, что вы обычно делаете с синхронным LINQ, вам понадобится System.Linq.Async.
Джон Скит