【发布时间】:2020-06-28 16:59:15
【问题描述】:
不可能两次枚举IAsyncEnumerable?
CountAsync 运行后,await foreach 将不会枚举任何项目。为什么?
AsyncEnumerator上好像没有Reset方法。
var count = await itemsToImport.CountAsync();
await foreach (var importEntity in itemsToImport)
{
// won't run
}
数据来源:
private IAsyncEnumerable<TEntity> InternalImportFromStream(TextReader reader)
{
var csvReader = new CsvReader(reader, Config);
return csvReader.GetRecordsAsync<TEntity>();
}
【问题讨论】:
-
即使是同步枚举器也几乎从未真正实现
.Reset();IAsyncEnumerator只是编纂了您不能多次列举的做法。至于IAsyncEnumerable,同样适用于IEnumerable:是否可以多次枚举在接口中没有定义,但对于许多来源你不能,因为会有隐藏的性能损失或不一致的结果(就像执行两次数据库查询一样)。您必须明确处理,这意味着您必须实现结果(.ToList()和相关)或自己重做操作。 -
如果您不需要开始计数,那么您可以在将计数递增 1 的同时迭代每个项目,因此您不必将所有内容都放入内存,同时仍然获得计数一次'已经处理了集合。
-
对于某些来源,您无法在枚举“之前”获得计数(例如,从数据库中流式传输行)。
.Reset(),即使它存在,也要求将结果缓冲在某处,否则一切都将重做。如果你想自己缓冲/重做它们,你可以,但你不能指望源在你想等待计数的情况下为你做这件事。集合实现了自己的.Count属性,您可以使用这些属性“直接”获取计数;类似的机制可用于其他来源,具体取决于它们的性质(例如在 Web API 上单独调用Count)。 -
如果数据实际上已经加载(例如,驻留在内存中),则异步枚举它无论如何都不会添加任何内容,您不妨使用同步枚举(大概可以重做)。另一方面,如果它不是常驻的并且使用异步枚举来封装异步(文件)I/O,这会更常见,你必须有意识地选择两次读取文件,并且接口强制你明确地这样做。
-
正如大卫布朗所说,这不是因为
IAsyncEnumerable,而是因为实现它的底层类型。我建议阅读这篇文章。有一个例子不管它被迭代多少次都有效:docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/…
标签: c# async-await c#-8.0 iasyncenumerable