使用 IEnumerable.Aggregate 进行异步调用
Using IEnumerable.Aggregate with asynchronous calls
我正在尝试使用 LINQ IEnumerable.Aggregate 函数来创建一个由通过异步调用检索到的文件组成的字符串。不能百分百确定这是可能的,而且我也知道还有其他解决方案,但我想试一试。
现在我的代码是这样的:
private static async Task<string> GetFiles(IEnumerable<string> filePaths)
{
return filePaths.Aggregate(async (current, path) => current + await GetFile(path));
}
但是方法调用中的 "async" 错误标记为 "the return of an async method must be void, Task, or Task"。我一般都会遇到这个错误,但我不确定如何安排这个特定案例来避免它。有什么想法吗?
更新:
澄清一下,GetFile() 方法确实是异步的并且 returns Task<string>
:
private static async Task<string> GetFile(string filePath) { ... }
无需深入了解具体代码,但对于那些感兴趣的人,它使用 HttpClient.GetAsync(filePath)
和 returns 它的 response.Content.ReadAsStringAsync().Result
.
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);
}
正如@Sriram 所说,LINQ 和 async-await
不能很好地协同工作,因为没有对异步任务委托的内置支持。
您可以做的是自己创建聚合的异步重载:
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;
}
}
}
现在您可以执行以下操作:
private static Task<string> GetFiles(IEnumerable<string> filePaths)
{
return filePaths.AggregateAsync(async (current, path) => current +
await GetFile(path));
}
如果你想在 Aggregate
中使用 async
,你必须意识到任何异步的东西总是 returns 一个任务。考虑到这一点,很明显您的 Aggregate
调用的结果也应该是一个任务。
例如计算异步返回的一组数字的总和:
private static async Task<int> GetSumAsync(IEnumerable<Task<int>> numbers) {
return await numbers
.Aggregate(Task.FromResult(0), async (sumSoFar, nextNumber) => (await sumSoFar) + (await nextNumber));
}
我有点不明白您到底希望用 GetFiles 方法做什么。您确实意识到 Aggregate 将集合简化为一件事,对吗? ('sum' 函数就是一个很好的例子)
我正在尝试使用 LINQ IEnumerable.Aggregate 函数来创建一个由通过异步调用检索到的文件组成的字符串。不能百分百确定这是可能的,而且我也知道还有其他解决方案,但我想试一试。
现在我的代码是这样的:
private static async Task<string> GetFiles(IEnumerable<string> filePaths)
{
return filePaths.Aggregate(async (current, path) => current + await GetFile(path));
}
但是方法调用中的 "async" 错误标记为 "the return of an async method must be void, Task, or Task"。我一般都会遇到这个错误,但我不确定如何安排这个特定案例来避免它。有什么想法吗?
更新:
澄清一下,GetFile() 方法确实是异步的并且 returns Task<string>
:
private static async Task<string> GetFile(string filePath) { ... }
无需深入了解具体代码,但对于那些感兴趣的人,它使用 HttpClient.GetAsync(filePath)
和 returns 它的 response.Content.ReadAsStringAsync().Result
.
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);
}
正如@Sriram 所说,LINQ 和 async-await
不能很好地协同工作,因为没有对异步任务委托的内置支持。
您可以做的是自己创建聚合的异步重载:
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;
}
}
}
现在您可以执行以下操作:
private static Task<string> GetFiles(IEnumerable<string> filePaths)
{
return filePaths.AggregateAsync(async (current, path) => current +
await GetFile(path));
}
如果你想在 Aggregate
中使用 async
,你必须意识到任何异步的东西总是 returns 一个任务。考虑到这一点,很明显您的 Aggregate
调用的结果也应该是一个任务。
例如计算异步返回的一组数字的总和:
private static async Task<int> GetSumAsync(IEnumerable<Task<int>> numbers) {
return await numbers
.Aggregate(Task.FromResult(0), async (sumSoFar, nextNumber) => (await sumSoFar) + (await nextNumber));
}
我有点不明白您到底希望用 GetFiles 方法做什么。您确实意识到 Aggregate 将集合简化为一件事,对吗? ('sum' 函数就是一个很好的例子)