创建空的 IAsyncEnumerable

Create empty IAsyncEnumerable

我有一个这样写的界面:

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

我想写一个 returns 没有项目的空实现,像这样:

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

有什么想法吗?

如果您安装了 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
    }
}

如果出于任何原因你不想安装 Jon 的回答中提到的包,你可以像这样创建方法 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>() 的简要实现,适用于您需要它而您 cannot/don 不想使用该包的情况。您可以找到包 here.

中使用的实现

我想避免安装新包,但 实际上并未按照原始问题中的要求实现 IAsyncEnumerable<T>。这是一个完整的解决方案,它实现了该接口,可以轻松地允许调用 AsyncEnumerable.Empty<T> 的方式与 Enumerable.Empty<T> 今天的工作方式相同。

public static class AsyncEnumerable
{
    /// <summary>
    /// Creates an <see cref="IAsyncEnumerable{T}"/> which yields no results, similar to <see cref="Enumerable.Empty{TResult}"/>.
    /// </summary>
    public static IAsyncEnumerable<T> Empty<T>() => EmptyAsyncEnumerator<T>.Instance;

    private class EmptyAsyncEnumerator<T> : IAsyncEnumerator<T>, IAsyncEnumerable<T>
    {
        public static readonly EmptyAsyncEnumerator<T> Instance = new EmptyAsyncEnumerator<T>();
        public T Current => default;
        public ValueTask DisposeAsync() => default;
        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            return this;
        }
        public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(false);
    }
}