You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:45:40