如何将List<Task>多类型任务转为Task<List<object>>并按序返回结果?
核心问题
你当前实现的泛型版本RunTasks<T>仅支持所有传入任务返回同一类型T的场景,无法满足不同返回类型任务混传的需求——Task<int>、Task<string>是完全独立的封闭泛型类型,不存在可以同时兼容二者的公共泛型参数T,强行混传会出现编译错误或运行时强转失败。
正确实现方案
不需要为方法定义泛型参数,直接用非泛型IList<Task>作为入参类型即可:所有Task<TResult>都继承自非泛型Task基类,不同返回类型的任务都可以直接传入该列表,且传入后任务会立即并行执行,无需额外调度。
所有任务执行完成后,有两种成熟方式获取各任务的返回值:
方案1:反射实现(代码最简单,适合任务量小的场景)
直接通过反射读取每个具体Task<T>的Result属性即可,不需要统一强转类型:
private async Task<IList<object>> RunTasks(IList<Task> taskList) { if (taskList == null || taskList.Count == 0) { return new List<object>(); } // 等待所有任务执行完成,若需要捕获异常而非直接抛出,可在此处加try-catch包裹 await Task.WhenAll(taskList); var resultArr = new object[taskList.Count]; for (int i = 0; i < taskList.Count; i++) { var currentTask = taskList[i]; // 处理任务故障、取消的场景,可按需替换为返回异常对象等逻辑 if (currentTask.IsFaulted || currentTask.IsCanceled) { resultArr[i] = default; continue; } // 无返回值的非泛型Task直接返回null var taskRealType = currentTask.GetType(); if (!taskRealType.IsGenericType || taskRealType.GetGenericTypeDefinition() != typeof(Task<>)) { resultArr[i] = default; continue; } // 读取当前任务的返回值 var resultProp = taskRealType.GetProperty("Result"); resultArr[i] = resultProp.GetValue(currentTask); } return resultArr; }
调用示例:
var taskList = new List<Task> { Task.FromResult(1024), // 返回int Task.FromResult("测试字符串"), // 返回string Task.FromResult(new { Id = 1, Name = "测试对象" }) // 返回匿名类型 }; var results = await RunTasks(taskList); // 结果顺序和传入顺序完全一致:results[0]是int,results[1]是string,results[2]是匿名对象
方案2:预包装取值逻辑(无反射开销,性能更高,适合任务量大的场景)
传入任务时提前将对应类型的结果取值逻辑封装好,完全避免反射开销:
private class TaskWithResultSelector { public Task RunningTask { get; init; } public Func<object> GetResult { get; init; } } private async Task<IList<object>> RunTasks(IList<TaskWithResultSelector> taskList) { if (taskList == null || taskList.Count == 0) { return new List<object>(); } await Task.WhenAll(taskList.Select(t => t.RunningTask)); var resultArr = new object[taskList.Count]; for (int i = 0; i < taskList.Count; i++) { var item = taskList[i]; resultArr[i] = item.RunningTask.IsFaulted || item.RunningTask.IsCanceled ? default : item.GetResult(); } return resultArr; }
调用示例:
var intTask = Task.FromResult(1024); var stringTask = Task.FromResult("测试字符串"); var taskList = new List<TaskWithResultSelector> { new() { RunningTask = intTask, GetResult = () => intTask.Result }, new() { RunningTask = stringTask, GetResult = () => stringTask.Result } }; var results = await RunTasks(taskList);
疑问明确答复
- 只要需要支持不同返回类型的任务混传,不需要定义泛型参数
T。如果所有任务返回类型一致,可以保留泛型参数,调用时传入对应的返回值类型即可,比如全是Task<string>就传T=string。 - 不使用泛型参数时,要么通过反射动态读取每个任务的
Result属性,要么在传入任务时提前封装强转取值的逻辑,即可拿到每个任务的真实返回值,直接存入object数组即可,不需要做统一类型转换。 - 你原有代码的问题除了泛型限制外,还缺少对任务取消状态(
IsCanceled)的判断,若任务被取消,直接强转访问Result会抛出异常。
内容的提问来源于stack exchange,提问作者Programmerzzz
相关产品推荐
相关产品推荐

