如何将IEnumerable<Task<string>>转换为IEnumerable<string>?
问题描述
我有一组作为对象ID的字符串集合,需要检查它们是否存在于数据库中。编写了如下代码:
var results = new List<string> { "123456", "654321" } .Select(async s => { // 检查数据库 return "The result"; });
得到的类型是IEnumerable<Task<string>>,但我需要的是IEnumerable<string>。我搞不懂Task是哪来的——明明lambda返回的是string啊。如果去掉lambda的async修饰符,就能得到IEnumerable<string>,但这样又没法在lambda里await数据库调用。
想搞清楚两个问题:
- 为什么会返回
IEnumerable<Task<string>>? - 怎么写代码才能得到
IEnumerable<string>?
问题解答
一、为什么返回IEnumerable<Task<string>>?
只要给lambda加上async修饰符,这个委托就自动变成了异步方法,而异步方法的返回规则是固定的:
- 如果你写了
return T(比如这里的string),异步方法实际会返回Task<T> - 如果没有返回值(void),异步方法返回
Task
简单说,async直接改变了整个委托的返回类型——不管你内部return什么,带async的方法都会把返回值包装成Task。所以Select遍历每个元素时,每个委托都返回Task<string>,最终就得到了IEnumerable<Task<string>>。
二、如何得到IEnumerable<string>?
核心是要等待所有Task<string>执行完成,提取出里面的string结果,分两种常见场景:
1. 串行执行(逐个等待)
如果不想同时发起大量数据库请求,避免压垮数据库,可以逐个await每个Task:
// 先把所有异步任务收集起来 var taskList = new List<string> { "123456", "654321" } .Select(async s => { // 这里写带await的数据库查询逻辑,比如: // var exists = await _dbContext.Entities.AnyAsync(e => e.Id == s); return $"ID {s} 的检查结果"; }) .ToList(); var results = new List<string>(); // 逐个等待任务完成,把结果加入列表 foreach (var task in taskList) { results.Add(await task); }
2. 并行执行(同时发起请求)
如果追求效率,想让所有数据库请求同时执行,可以用Task.WhenAll:
var tasks = new List<string> { "123456", "654321" } .Select(async s => { // 带await的数据库查询逻辑 return $"ID {s} 的检查结果"; }); // 等待所有任务完成,返回的results是string[],本身就实现了IEnumerable<string> var results = await Task.WhenAll(tasks);
注意:这种方式会一次性发起所有异步请求,要考虑数据库的连接池上限,避免请求过多导致连接超时。
内容的提问来源于stack exchange,提问作者DreamingOfSleep
相关产品推荐
相关产品推荐

