【发布时间】:2018-05-15 07:57:12
【问题描述】:
我有一个获取一系列项目的函数:
List<MyType> GetPage(int pageNr) {...}
要获取所有可用项目,您可以执行以下操作:
IEnumerable<MyType> FetchMyItems()
{
int pageNr = 0;
var fetchedItems = GetPage(pageNr);
while (fetchedItems.Any()
{ // there are items to return
// yield return the fetched items:
foreach (var fetchedItem in fetchedItems)
yield return fetchedItems;
// get the next page
++pageNr;
fetchedItems = GetPage(pageNr)
}
}
使用此方法,我的用户将不再需要获取每页的项目,如果他们只需要几个项目,则只会获取第一页。当他们需要更多项目时,将自动获取更多页面。缺点当然是我有时会多取一些不会用到的物品。
我想要一个异步版本
所以我有以下GetPage:
async Task<List<MyType>> GetPageAsync(int pageNr) {...}
根据我对 IEnumerable 的了解,它不会创建结果序列,它只会创建枚举此序列的可能性。
这种枚举是使用 'foreach' 显式完成的,或隐式使用 'ToList'、'ToArray' 等 LINQ 函数,但也使用 'FirstOrDefault'、'Any'、Sum 等函数。
var fetchItemTasks = FetchMyItemsAsync();
foreach (var fetchItemTask in fetchedItemTasks)
{
MyType fetchedItem = await fetchItemTask;
Process(fetchedItem);
}
或者如果你做这个低级:
var myitemsEnumerable = fetchMyItemsAsync();
// don't await yet, only create the possibility to enumerate
var enumerator = myItemsEnumerable.GetEnumerator();
while (enumerator.MoveNext())
{ // there is an item available in current:
Task<MyType> task = enumerator.Current;
return task;
}
看这个,看来这个函数不应该返回Task<IEnumerable<MyType>>而是IEnumerable<Task<MyType>>,如果你想访问一个元素你必须等待这个。
因此,返回枚举等待项的可能性的函数本身并不是异步的。您无需等待可枚举,无需访问慢速提供程序(如数据库)即可创建此可枚举。可枚举序列中的项是可等待的:
IEnumerable<Task<MyType>> FetchMyItems()
{
int pageNr = 0;
Task<List<MyType>> fetchItemTask = GetPageAsync(pageNr);
现在呢?如果我await fetchItemTask,这些项目已经是本地的,没有什么可以等待的了。如果我fetchItemTask.Wait(),功能块。
【问题讨论】:
-
这些项目已经是本地的,没有什么可以等待的了这是什么意思?
-
你需要的是异步枚举器,它还没有语言支持(这里有一个建议:github.com/dotnet/csharplang/blob/master/proposals/…),但是你当然可以在没有这种支持的情况下使用这种模式(没有 foreach,还有更多详细的语法)。
-
最后我想创建扩展函数,如
ToListAsync、ToArrayAsync、CountAsync、AnyAsync等。但对于它们,我需要一个基本的 IEnumerable。我会阅读您的建议并检查是否可以创建我自己的EnumeratorAsync<TSource>
标签: c# async-await ienumerable