【问题标题】:Pass-through for IAsyncEnumerable? [duplicate]IAsyncEnumerable 的传递?
【发布时间】:2020-05-09 14:22:15
【问题描述】:

我想知道是否可以编写一个函数来“通过”一个 IAsyncEnumerable...也就是说,该函数将调用另一个 IAsyncEnumerable 函数并产生所有结果,而无需编写 foreach做吗?

我发现自己经常编写这种 Code Pattern。这是一个例子:

async IAsyncEnumerable<string> MyStringEnumerator();

async IAsyncEnumerable<string> MyFunction()
{
   // ...do some code...

   // Return all elements of the whole stream from the enumerator
   await foreach(var s in MyStringEnumerator())
   {
      yield return s;
   }
}

无论出于何种原因(由于分层设计),我的函数MyFunction 想要调用MyStringEnumerator,但随后无需干预即可产生所有内容。我必须继续编写这些foreach 循环才能做到这一点。如果是IEnumerable,我会返回IEnumerable。如果是 C++,我可以写一个宏来做。

最佳做法是什么?

【问题讨论】:

  • 您的...do some code... 是否致电yield return?是await吗?
  • MyStringEnumerator() 里面有什么?
  • 为什么不只是return MyStringEnumerator();
  • 可能重复:Return IAsyncEnumerable from an async method。简短的回答:C# 中不支持省略 await foreach 循环。

标签: c# c#-8.0 iasyncenumerable


【解决方案1】:

如果它是 IEnumerable,我会返回 IEnumerable。

好吧,你可以用IAsyncEnumerable做同样的事情(注意async被删除了):

IAsyncEnumerable<string> MyFunction()
{
 // ...do some code...

 // Return all elements of the whole stream from the enumerator
 return MyStringEnumerator();
}

但是,这里有一个重要的语义考虑。调用枚举器方法时,...do some code...立即执行,而在枚举器被枚举时。

// (calling code)
var enumerator = MyFunction(); // `...do some code...` is executed here
...
await foreach (var s in enumerator) // it's not executed here when getting the first `s`
  ...

同步和异步可枚举都是如此。

如果您希望在枚举枚举器时执行...do some code...,那么您需要使用foreach/yield 循环来获取延迟执行语义:

async IAsyncEnumerable<string> MyFunction()
{
 // ...do some code...

 // Return all elements of the whole stream from the enumerator
 await foreach(var s in MyStringEnumerator())
   yield return s;
}

如果您也想要具有同步枚举的延迟执行语义,那么您将不得不在同步世界中使用相同的模式:

IEnumerable<string> ImmediateExecution()
{
 // ...do some code...

 // Return all elements of the whole stream from the enumerator
 return MyStringEnumerator();
}

IEnumerable<string> DeferredExecution()
{
 // ...do some code...

 // Return all elements of the whole stream from the enumerator
 foreach(var s in MyStringEnumerator())
   yield return s;
}

【讨论】:

  • 取消令牌语义和使用 EnumeratorCancellation 属性是否能与立即选项和延迟选项一起正常工作?也许上面的例子可以扩展来展示这一点?
  • @YevgeniyP:更现实的解决方案是采用 [EnumeratorCancellation] CancellationToken 并通过它。
【解决方案2】:

从调用方法返回Task&lt;IAsyncEnumerable&lt;Obj&gt;&gt; 似乎有效

async IAsyncEnumerable<string> MyStringEnumerator();

async Task<IAsyncEnumerable<string>> MyFunction()
{
    await Something();

    return MyStringEnumerator();
}

然后您需要等待 MyFunction()。因此,在异步 foreach 中使用将是

await foreach (string s in await MyFunction()) {}

【讨论】:

  • 这正是我正在寻找的!谢谢。
猜你喜欢
  • 2020-09-27
  • 2020-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-04
  • 2020-09-08
  • 2020-06-02
相关资源
最近更新 更多