如何将Task<List<Derived>>转换为Task<List<BaseType>>以统一存储任务?
如何将
Task<List<Derived>>转换为Task<List<BaseType>>? 嘿,这个问题我之前也碰到过——泛型协变在Task和List组合的时候确实会有点“水土不服”,我来给你捋清楚怎么解决!
首先得说清楚为什么直接转换行不通:虽然Task<T>支持协变,Derived也继承自BaseType,但List<T>本身是不变类型,也就是说List<Derived>和List<BaseType>是完全独立的类型,没办法直接通过协变自动转换。所以Task<List<Derived>>不能直接赋值给Task<List<BaseType>>。
不过解决方法有好几种,看你偏好哪种:
方法1:用await异步转换(最推荐)
这种方式代码简洁,还能自动处理异常和异步上下文,是现代C#异步编程的首选:
// 先写一个转换方法 async Task<List<BaseType>> ConvertDerivedTaskToBase(Task<List<Derived>> derivedTask) { var derivedList = await derivedTask; // 将List<Derived>转成List<BaseType> return derivedList.Cast<BaseType>().ToList(); } // 然后把你的多个任务批量转换 List<Task<List<Derived>>> derivedTasks = GetAllDerivedTasks(); List<Task<List<BaseType>>> baseTasks = derivedTasks.Select(ConvertDerivedTaskToBase).ToList();
方法2:用ContinueWith链式转换
如果你习惯用传统的Task链式写法,也可以用ContinueWith来处理,但要注意它的上下文行为(比如默认不在原上下文执行):
Task<List<Derived>> derivedTask = GetDerivedListAsync(); Task<List<BaseType>> baseTask = derivedTask.ContinueWith(t => t.Result.Cast<BaseType>().ToList(), // 如果需要在原同步上下文执行,可以加这个选项 TaskContinuationOptions.ExecuteSynchronously);
方法3:写通用扩展方法(复用性拉满)
如果需要在多个地方重复转换,可以封装一个扩展方法,让代码更优雅:
public static class TaskExtensions { public static async Task<List<TBase>> ToBaseListTask<TDerived, TBase>(this Task<List<TDerived>> task) where TDerived : TBase { var derivedList = await task; return derivedList.Cast<TBase>().ToList(); } } // 使用的时候就很简洁 var baseTask = derivedTask.ToBaseListTask<Derived, BaseType>(); // 甚至可以靠类型推断省略部分泛型参数 var baseTask = derivedTask.ToBaseListTask<BaseType>();
额外小技巧:如果能接受IEnumerable<BaseType>
如果你不需要严格的List,而是可以用IEnumerable<BaseType>的话,那事情就简单多了——因为IEnumerable<T>是协变的,Task<IEnumerable<BaseType>>可以直接接收Task<List<Derived>>:
Task<IEnumerable<BaseType>> baseTask = derivedTask;
这种情况不需要任何转换,直接赋值就行,省了不少事!
总的来说,核心思路就是先获取Task<List<Derived>>的结果,把List<Derived>转换为List<BaseType>,再包装回Task里。用await的方式是最直观也最不容易踩坑的,推荐优先用这个。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

