异步任务 async Task<IEnumerable<T>> 抛出了 "is not an iterator interface type" 错误。

23
以下代码在我使用async await并将IEnumerable包装在Task中时,会抛出is not an iterator interface type的错误。如果我删除async await,它将与IEnumerable<List<T>>一起工作。
private async Task<IEnumerable<List<T>>> GetTableDataAsync<T>(CloudTable cloudTable, TableQuery<T> tableQuery)
        where T : ITableEntity, new()
    {
        TableContinuationToken contineousToken = null;
        do
        {
            var currentSegment = await GetAzureTableDateAsync(cloudTable, tableQuery, contineousToken);
            contineousToken = currentSegment.ContinuationToken;
            yield return currentSegment.Results;

        } while (contineousToken != null);

    }

虽然我可以考虑使用 Rx,但我不确定是什么导致了这个问题。

2个回答

28

这是一个旧问题,接受的答案是正确的,但现在随着c#8的推出,引入了IAsyncEnumerable。因此,您应该使用IAsyncEnumerable而不是IEnumerable。请查看文档:https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#asynchronous-streams

示例:

public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
{
    for (int i = 0; i < 20; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

呼叫代码:

await foreach (var number in GenerateSequence())
{
    Console.WriteLine(number);
}

1
非常高兴了解到IAsyncEnumerable!这太棒了!!我希望早些知道它!! - Shintaro Takechi

19

只有声明返回 IEnumerable<T>IEnumerableIEnumerator 或者 IEnumerator<T> 的方法才能使用迭代器块来实现,这就排除了所有异步方法。

从本质上讲,不清楚它们如何工作,因为 IEnumerable<T> 是基于拉取的,而异步方法更具有响应性。此外,迭代器块的目的是让调用者能看到中间结果——而异步方法返回的任务将一直等到异步方法完成才会完成。

您需要采用另一种替代方法——无论是 Rx 还是其他方法。您可能首先要考虑的不是实现的样子,而是调用者的操作。也许您实际上想要一个 IEnumerable<Task<List<T>> 吗?


也许 "IEnumerable<Task<List<T>>" 可以胜任。要求是读取所有 Azure 表的值并得出一些指标(计数、总和),表的数量超过 4k,给定筛选条件的记录将达到数百万。我不想浪费线程等待从 Azure 返回数据。 - Saravanan
@Saravanan:所以你需要考虑一下客户端代码的样子。完全有可能迭代器块在这里并不会真正帮助你,因为它们是阻塞式拉模型。 - Jon Skeet
已经开始使用 Rx。最近发布了一个 NuGet 包,使用 WindowsAzure.Storage sdk 提供了一个响应式接口来访问表存储 - Reactive.AzureStorage.Table - Saravanan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接