Return 来自异步方法的 IAsyncEnumerable

Return IAsyncEnumerable from an async method

采取以下方法:

public async IAsyncEnumerable<int> Foo()
{
   await SomeAsyncMethod();
   return Bar(); // Throws since you can not return values from iterators
}

public async IAsyncEnumerable<int> Bar()
{
   for(int i = 0; i < 10; i++)
   {
       await Task.Delay(100);
       yield return i;
   }
}

我想知道最好的做法是什么,上面的代码试图做什么。基本上从 async 方法返回 IAsyncEnumerable

对于我自己,我可以想象两种方式:

  1. 遍历 IAsyncEnumerable 并立即返回结果。
await foreach(var item in Bar())
{
    yield return item;
}
  1. 创建一个可以临时存储 IAsyncEnumerable 的结构,这似乎是更好的解决方案,但仍然有点矫枉过正。
return new AsyncEnumerableHolder<int>(Bar());

public struct AsyncEnumerableHolder<T>
{
    public readonly IAsyncEnumerable<T> Enumerable;

    public AsyncEnumerableHolder(IAsyncEnumerable<T> enumerable)
    {
        Enumerable = enumerable;
    }
}

有没有更好的方法来实现这种行为?

结构方法行不通。如果您想异步 return 一个 IAsyncEnumerator<T> 值,您 可以 使用 Task<IAsyncEnumerator<T>>return Bar();。然而,那是不寻常的。在异步可枚举的开头创建一个包含 await SomeAsyncMethod() 的新 IAsyncEnumerator<T> 会自然得多。为此,您应该按照选项 (1) 的建议使用 awaityield

public async IAsyncEnumerable<int> Foo()
{
  await SomeAsyncMethod();
  await foreach (var item in Bar())
    yield return item;
}

附带说明一下,JavaScript 对于这种 "enumerate this whole sequence into my result sequence" 概念有一个非常好的 yield* 语法,它支持同步和异步序列。 C# 没有这种用于同步或异步序列的语法。