为什么 Task<T> 不是协变的?
Why is Task<T> not co-variant?
class ResultBase {}
class Result : ResultBase {}
Task<ResultBase> GetResult() {
return Task.FromResult(new Result());
}
编译器告诉我它不能将 Task<Result>
隐式转换为 Task<ResultBase>
。有人可以解释这是为什么吗?我本以为协方差能让我以这种方式编写代码。
根据someone who may be in the know...
The justification is that the advantage of covariance is outweighed by
the disadvantage of clutter (i.e. everyone would have to make a
decision about whether to use Task or ITask in every single
place in their code).
在我看来,无论哪种方式都没有非常令人信服的动机。 ITask<out T>
将需要很多新的重载,可能在幕后相当多(我无法证明实际基础 class 是如何实现的,或者它与天真的实现相比有多么特别)但是更多以这些 linq
类扩展方法的形式。
其他人提出了一个很好的观点——把时间花在使 class
es 协变和逆变上会更好。我不知道那会有多难,但对我来说这听起来像是更好地利用时间。
另一方面,有人提到在 async
方法中提供真正的 yield return
类功能会非常酷。我的意思是,没有技巧。
我意识到我来晚了,但这是我一直用来解决这个缺失功能的扩展方法:
/// <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 async Task<TResult> AsTask<T, TResult>(this Task<T> task)
where T : TResult
where TResult : class
{
return await task;
}
这样你就可以做到:
class ResultBase {}
class Result : ResultBase {}
Task<Result> GetResultAsync() => ...; // Some async code that returns Result
Task<ResultBase> GetResultBaseAsync()
{
return GetResultAsync().AsTask<Result, ResultBase>();
}
在我的例子中,我在编译时不知道 Task 泛型参数,必须使用 System.Threading.Tasks.Task base class。这是我根据上面的示例创建的解决方案,也许会对某人有所帮助。
[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);
}
我在 MorseCode.ITask NuGet package 上取得了成功。此时它非常稳定(几年内没有更新)但安装起来很简单,从 ITask 转换为 Task 所需要做的唯一事情就是调用 .AsTask()
(反向扩展方法也是随包裹发货)。
class ResultBase {}
class Result : ResultBase {}
Task<ResultBase> GetResult() {
return Task.FromResult(new Result());
}
编译器告诉我它不能将 Task<Result>
隐式转换为 Task<ResultBase>
。有人可以解释这是为什么吗?我本以为协方差能让我以这种方式编写代码。
根据someone who may be in the know...
The justification is that the advantage of covariance is outweighed by the disadvantage of clutter (i.e. everyone would have to make a decision about whether to use Task or ITask in every single place in their code).
在我看来,无论哪种方式都没有非常令人信服的动机。 ITask<out T>
将需要很多新的重载,可能在幕后相当多(我无法证明实际基础 class 是如何实现的,或者它与天真的实现相比有多么特别)但是更多以这些 linq
类扩展方法的形式。
其他人提出了一个很好的观点——把时间花在使 class
es 协变和逆变上会更好。我不知道那会有多难,但对我来说这听起来像是更好地利用时间。
另一方面,有人提到在 async
方法中提供真正的 yield return
类功能会非常酷。我的意思是,没有技巧。
我意识到我来晚了,但这是我一直用来解决这个缺失功能的扩展方法:
/// <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 async Task<TResult> AsTask<T, TResult>(this Task<T> task)
where T : TResult
where TResult : class
{
return await task;
}
这样你就可以做到:
class ResultBase {}
class Result : ResultBase {}
Task<Result> GetResultAsync() => ...; // Some async code that returns Result
Task<ResultBase> GetResultBaseAsync()
{
return GetResultAsync().AsTask<Result, ResultBase>();
}
在我的例子中,我在编译时不知道 Task 泛型参数,必须使用 System.Threading.Tasks.Task base class。这是我根据上面的示例创建的解决方案,也许会对某人有所帮助。
[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);
}
我在 MorseCode.ITask NuGet package 上取得了成功。此时它非常稳定(几年内没有更新)但安装起来很简单,从 ITask 转换为 Task 所需要做的唯一事情就是调用 .AsTask()
(反向扩展方法也是随包裹发货)。