【问题标题】:如何同步运行异步枚举器方法并将其存储为 IEnumerable?
【发布时间】:2022-01-23 07:29:08
【问题描述】:

我有一个异步方法,它使用 yield 返回一个 IAsyncEnumerable。在某些情况下,可能需要同步获取项目,因此我想为此创建另一个方法,通过同步运行现有方法返回 IEnumerable 以避免重复代码。

async IAsyncEnumerable<Foo> GetItemsAsync()
{
    yield return ...
}

IEnumerable<Foo> GetItems() => GetItemsAsync() // Run shyncronously

我阅读了大量关于使用诸如 Task.RunSynchronously 之类的技术同步运行异步方法的解决方案,或者只是在同步上下文中调用它并忽略警告。但是,在异步枚举的情况下,这些似乎都不可能。该接口没有 RunSynchronously 方法,并且尝试使用未等待的 foreach 对其进行枚举会导致编译错误。获取枚举器并同步运行 MoveNext 似乎也不是一个选项,因为该方法返回的 ValueTask 没有等待方法,我需要捕获任务的结果以知道何时停止迭代。

那么是否可以同步强制我的异步枚举器方法并将结果作为 IEnumerable,如果可以,如何?

【问题讨论】:

  • 这是一个更普遍的问题“我如何从同步代码中调用异步代码?”的具体案例。最好的答案是你没有,但如果你,有various hacks available

标签: c# .net asynchronous task iasyncenumerable


【解决方案1】:

您可以使用 System.Linq.Async 包中的 ToEnumerable 扩展方法。这个方法的签名是:

public static IEnumerable<TSource> ToEnumerable<TSource>(
    this IAsyncEnumerable<TSource> source)

...你可以这样使用它:

IEnumerable<Foo> GetItems() => GetItemsAsync().ToEnumerable();

更新:具有相同功能的扩展方法ToBlockingEnumerable 可能包含在 .NET 平台的下一版本 (.NET 7) 中。相关 GitHub 问题:Add an IAsyncEnumerable.ToEnumerable extension method

【讨论】:

  • 谢谢。但是,这似乎不适用于 .net 5。我一直在考虑切换到 .net 6 一段时间,我可能不得不这样做才能使用它。提醒使用 .net 5 的任何人。
  • @TheBoxyBear AFAIKS 最新版本的 System.Linq.Async 包 (5.1.0) 在 .NET Core 3.1 及更高版本上受支持。其中包括 .NET 5。
  • 事实证明它在两个 .NET 版本中都受支持。它说它不适合我的原因是因为我在 using 语句中省略了 System.Linq,但 .NET 6 隐式添加了它。
猜你喜欢
  • 2011-07-02
  • 1970-01-01
相关资源
最近更新 更多