C#:如何将Task <t []>转换为Task <t>的IEnumerable

时间:2019-05-04 22:15:09

标签: c# .net async-await

我有Nito.AsyncEx.AsyncCollection<MyItem[]>,我想将其包装成类似IAsyncEnumerable<MyItem>的东西。

目前,我仅使用IEnumerable<Task<T>>而不是IAsyncEnumerable

我需要像SelectMany这样的东西来平整列表。这就是问题。以下代码不起作用。

能否使其正常工作?

public static IEnumerable<Task<T>> Flatten<T>(IEnumerable<Task<T[]>> source) {
    foreach (Task<T[]> task in source) {
        // We should convert Task<T[]> to IEnumerable of Task<T>
        T[] result = await task;
        foreach (T item in result) {
            yield return Task.FromResult( item );
        }
    }
}

2 个答案:

答案 0 :(得分:1)

最好的解决方案是等待几个月,直到IAsyncEnumerable<T>成为现实。第二好的解决方案是使用System.Interactive.Async中的IAsyncEnumerable<T>。但是现在...

IEnumerable<T>是同步拉式迭代器。因此,它必须同步提供其T实例。对于这个问题,更重要的是,它必须同步提供实例的 count

Task<T>是单个项目的异步提取。它将仅异步提供其T实例。

这就是问题所在:您想要的结果IEnumerable<Task<T>>必须能够:

  1. 同步提供每个Task<T>。不是太难;您可以在绝对必要的情况下使用TaskCompletionSource<T>
  2. 同步提供其 count 。换句话说,它需要同步提供每个 Task<T>。他们全部。根据您的输入,这是不可能的。

您的输入是IEnumerable<Task<T[]>>。由于这是IEnumerable<>,因此您可以同步获取计数,但这只是Task<T[]>项的计数-即T[]项的计数。要进行展平操作,您必须awaitTask<T[]>中的每一个,以获得T个项目的计数。因此,您无法生成可以同步知道其有多少项的IEnumerable<Task<T>>

由于IEnumerable<Task<T>>IAsyncEnumerable<T> 不同,因此您遇到了这种类型限制。

您的选择是:

  1. 使用真实的IAsyncEnumerable<T>,自己编写或通过System.Interactive.Async编写。优点:适当的类型允许语义上精确的代码。缺点:IAsyncEnumerable<T>的生产和消费仍然很痛苦(目前)。
  2. 先进行所有异步工作。优点:更简单的代码。缺点:更改您所需的语义。

使用第二个选项,您的展平运算符可能看起来像这样:

public static async Task<IEnumerable<T>> Flatten<T>(IEnumerable<Task<T[]>> tasks) 
{
  var results = await Task.WhenAll(tasks);
  return results.SelectMany(x => x);
}

在您拥有真实的IAsyncEnumerable<T>之前,许多运营商将要求未来(Task<T>)像这样最终出现在外部。

答案 1 :(得分:-1)

像这样使用 定义Task并在其中定义您的类型并等待结果,确保您正在等待的是异步的。就像下面给出的方法一样。

public async Task<IEnumerable<T>> getIEnumerable () {
 return await yourresult.toListAsync();

}