使用IEnumerable.Aggregate与异步调用

Don*_*mon 6 .net c# linq asynchronous async-await

我正在尝试使用LINQ IEnumerable.Aggregate函数来创建一个由通过异步调用检索的文件组成的字符串.不是百分之百确定它是可能的,而且我也知道还有其他解决方案,但我想尝试一下.

现在我的代码看起来像这样:

private static async Task<string> GetFiles(IEnumerable<string> filePaths)
{
    return filePaths.Aggregate(async (current, path) => current + await GetFile(path));
}
Run Code Online (Sandbox Code Playgroud)

但是方法调用中的"异步"是错误标记,"异步方法的返回必须为void,Task或Task".我总体上得到了这个错误,但我不确定如何安排这个具体案例来避免它.有任何想法吗?

更新:为了澄清,GetFile()方法确实是异步的并返回Task<string>:

private static async Task<string> GetFile(string filePath) { ... }
Run Code Online (Sandbox Code Playgroud)

不需要进入特定的代码,但对于那些感兴趣的人使用HttpClient.GetAsync(filePath)和返回它response.Content.ReadAsStringAsync().Result.

Yuv*_*kov 8

正如@Sriram所说,LINQ并async-await没有很好地协同工作,因为没有内置的异步Task委托支持.

可以做的是自己创建聚合的异步重载:

public static class AsynchronousEnumerable
{
    public static async Task<TSource> AggregateAsync<TSource>
                                      (this IEnumerable<TSource> source,
                                       Func<TSource, TSource, Task<TSource>> func)
    {
       using (IEnumerator<TSource> e = source.GetEnumerator())
       {
            if (!e.MoveNext())
            {
                throw new InvalidOperationException("Sequence contains no elements");
            }

            TSource result = e.Current;
            while (e.MoveNext()) result = await func(result, e.Current);
            return result;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以执行以下操作:

private static Task<string> GetFiles(IEnumerable<string> filePaths)
{
    return filePaths.AggregateAsync(async (current, path) => current + 
                                                             await GetFile(path));
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的尝试。您的代码中有一个错误。在不调用 e.MoveNext() 并检查它是否返回 true 的情况下,您不能盲目访问 e.Current。我希望你能解决它。提前+1。 (2认同)

Sri*_*vel 7

Aggregate方法不会异步工作.它不支持基于任务的代理.您需要通过在调用Aggregate方法之前等待它来自己创建结果序列.

这样的事情应该有效:

private static async Task<string> GetFiles(IEnumerable<string> filePaths)
{
    var files = filePaths
        .Select(p => GetFile(p))
        .ToArray();
    var results = await Task.WhenAll(files);

    return results
        .Aggregate((current, path) => current + path);
}
Run Code Online (Sandbox Code Playgroud)