class ResultBase {}
class Result : ResultBase {}
Task<ResultBase> GetResult() {
return Task.FromResult(new Result());
}
编译器告诉我它不能隐式地将Task<Result>
转换为Task<ResultBase>
。有人可以解释为什么会这样吗?我希望协方差可以让我以这种方式编写代码。
答案 0 :(得分:21)
根据someone who may be in the know ...
理由是协方差的优势被超过了 杂乱的缺点(即每个人都必须做出一个 决定是否在每一个中使用Task或ITask 放在他们的代码中。)
对我来说,无论如何都没有非常令人信服的动机。 ITask<out T>
需要很多新的重载,可能需要相当多的重载(我无法证明实际的基类是如何实现的,或者它与一个简单的实现相比有多特别)但是更多的形式是这些linq
- 类似的扩展方法。
其他人提出了一个很好的观点 - 花费class
es协变和逆变的时间会更好。我不知道会有多难,但这对我来说听起来更好用。
另一方面,有人提到在yield return
方法中提供真正的async
类似功能会非常酷。我的意思是,没有狡猾的手。
答案 1 :(得分:9)
我意识到我已经迟到了,但这是我用来解释这个缺失功能的扩展方法:
/// <summary>
/// Casts the result type of the input task as if it were covariant
/// </summary>
/// <typeparam name="T">The original result type of the task</typeparam>
/// <typeparam name="TResult">The covariant type to return</typeparam>
/// <param name="task">The target task to cast</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task<TResult> AsTask<T, TResult>(this Task<T> task)
where T : TResult
where TResult : class
{
return task.ContinueWith(t => t.Result as TResult);
}
这样你就可以:
class ResultBase {}
class Result : ResultBase {}
Task<ResultBase> GetResult()
{
return Task.FromResult(new Result()).AsTask<Result, ResultBase>();
}
答案 2 :(得分:0)
在我的情况下,我在编译时不了解Task泛型参数,因此不得不使用System.Threading.Tasks.Task基类。这是我从上面的示例创建的解决方案,也许会对某人有所帮助。
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task<T> AsTask<T>(this Task task)
{
var taskType = task.GetType();
await task;
return (T)taskType.GetProperty("Result").GetValue(task);
}
答案 3 :(得分:0)
我在 MorseCode.ITask NuGet package 上取得了成功。在这一点上它非常稳定(几年内没有更新)但是安装很简单,从 ITask 转换为 Task 唯一需要做的就是调用 .AsTask()
(以及反向扩展方法也随包裹一起提供)。