我有一个简单的同步方法,如下所示:
public IEnumerable<Foo> MyMethod(Source src)
{
// returns a List of Oof objects from a web service
var oofs = src.LoadOofsAsync().Result;
foreach(var oof in oofs)
{
// transforms an Oof object to a Foo object
yield return Transform(oof);
}
}
由于该方法是Web应用程序的一部分,因此最好尽可能有效地使用所有资源。因此,我想将该方法更改为异步方法。最简单的选择是执行以下操作:
public async Task<IEnumerable<Foo>> MyMethodAsync(Source src)
{
var oofs = await src.LoadOofsAsync();
return oofs.Select(oof => Transform(oof));
}
我不是
async
/await
或IEnumerable
的专家。但是,据我了解,使用这种方法“杀死”了IEnumerable
的好处,因为等待Task直到加载整个集合,从而忽略了IEnumerable
集合的“懒惰”。在其他StackOverflow帖子中,我阅读了一些有关使用Rx.NET(或System.Reactive)的建议。快速浏览文档,我已经了解到
IObservable<T>
是IEnumerable<T>
的异步替代方案。但是,使用幼稚的方法并尝试键入以下命令是行不通的:public async IObservable<Foo> MyMethodReactive(Source src)
{
var oofs = await src.LoadOofsAsync();
foreach(var oof in oofs)
{
yield return Transform(oof);
}
}
我遇到了一个编译错误,即
IObservable<T>
既未实现GetEnumerator()
也未实现GetAwaiter()
-因此无法同时使用yield
和async
。我没有更深入地阅读Rx.NET的文档,因此我可能只是错误地使用了该库。但是我不想花时间学习一个新的框架来修改单个方法。使用新的possibilities in C# 7,现在可以实现自定义类型。因此,从理论上讲,我可以实现
IAsyncEnumerable
,它将定义GetEnumerator()
和GetAwaiter()
方法。但是,根据我以前的经验,我记得创建GetEnumerator()
的自定义实现的一次失败尝试……我最终得到了一个隐藏在容器中的简单List。因此,我们有4种可能的方法来解决任务:
IEnumerable
IEnumerable
包装在Task<T>
中IAsyncEnumerable
这些尝试中的每一种都有哪些优点和缺点?其中哪一个对资源利用影响最大?
最佳答案
在您的情况下,听起来最好的选择是Task<IEnumerable<T>>
。这是每个选项都擅长的地方:
Task<IEnumerable<T>>
用于当有I/O操作来获取集合时。等待I/O操作的线程可以在等待时在其上安排其他内容。这听起来像您的情况。
IAsyncEnumerable
用于当您有一个集合时,其中每个项目都将需要或生成一个异步任务。一个例子:遍历一组项目并对每个项目执行某种独特的数据库查询。如果您的Transform
实际上是与I/O绑定(bind)的异步方法,那么这可能更明智。 关于c# - 异步返回集合的各种方式(具有C#7功能),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45921385/