5

IAsyncEnumerable不可能运行两次枚举?

一旦CountAsync运行,await foreach将不会枚举任何项目。为什么?似乎没有重置方法AsyncEnumerator

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>();
}
4

2 回答 2

7

这与重置 IAsyncEnumerator 无关。此代码尝试生成第二个 IAsyncEnumerator,就像 IEnumerable.GetEnumerator() 一样,它只能在某些类型的集合上使用。如果 Enumerable(异步与否)是对某种只进数据结构的抽象,则 GetEnumerator/GetAsyncEnumerator 将失败。

即使它没有失败,有时也很昂贵。例如,它可能会在每次枚举时运行数据库查询或访问远程 API。这就是为什么 IEnumerable/IAsyncEnumerable 使函数的公共返回类型很差的原因,因为它们无法描述返回集合的功能,并且几乎唯一可以对值做的事情就是使用 .ToList/ToListAsync 实现它。

例如,这很好用:

static async IAsyncEnumerable<int> Col()
{
    for (int i = 1; i <= 10; i++)
    {
        yield return i;
    }
}
static void Main(string[] args)
{
    Run().Wait();
}
static async Task Run()
{

    var col = Col();

    var count = await col.CountAsync();
    await foreach (var dataPoint in col)
    {
        Console.WriteLine(dataPoint);
    }
}
于 2020-03-17T15:50:25.940 回答
1

似乎不可能通过接口本身重置 IAsyncEnumerable,因为 IAsyncEnumerator 接口上没有 Reset 方法。

在此特定示例中,第二个枚举不起作用,因为 IAsyncEnumerable 以 Stream 为目标。读取流后,位置光标将指向流的末端。如果您可以控制流或对它的引用(我没有),您可以再次将位置设置为 0 并再次枚举它。

我倾向于使用 ToListAsync,然后从其 Count 属性中获取计数并同步迭代这些项目,因为它们已经加载。

于 2020-03-17T15:46:46.290 回答