C# 为什么任务<;T>;不是共变异? class ResultBase{} 类结果:ResultBase{} 任务GetResult(){ 返回Task.FromResult(新结果()); }

C# 为什么任务<;T>;不是共变异? class ResultBase{} 类结果:ResultBase{} 任务GetResult(){ 返回Task.FromResult(新结果()); },c#,task,covariance,C#,Task,Covariance,编译器告诉我它不能隐式地将Task转换为Task。有人能解释为什么会这样吗?我希望协方差能使我以这种方式编写代码。根据 理由是协方差的优势被 杂乱的缺点(即每个人都必须 决定是否在每个任务中使用Task或ITask 放入他们的代码中) 在我看来,这两种方式都没有令人信服的动机ITask将需要大量新的重载,可能有点隐蔽(我无法证明实际的基类是如何实现的,或者与原始实现相比它有多特殊),但更多的是以这些类似于linq的扩展方法的形式实现的 其他人提出了一个很好的观点——时间最好花在使类es协变和逆变

编译器告诉我它不能隐式地将
Task
转换为
Task
。有人能解释为什么会这样吗?我希望协方差能使我以这种方式编写代码。

根据

理由是协方差的优势被 杂乱的缺点(即每个人都必须 决定是否在每个任务中使用Task或ITask 放入他们的代码中)

在我看来,这两种方式都没有令人信服的动机
ITask
将需要大量新的重载,可能有点隐蔽(我无法证明实际的基类是如何实现的,或者与原始实现相比它有多特殊),但更多的是以这些类似于
linq
的扩展方法的形式实现的

其他人提出了一个很好的观点——时间最好花在使
es协变和逆变上。我不知道这有多难,但对我来说,这似乎是更好地利用时间


另一方面,有人提到在
async
方法中有一个真正的
yield-return
类特性是非常酷的。我的意思是,没有花招。

我意识到我参加聚会迟到了,但我一直在使用一种扩展方法来解释这个缺失的功能:

class ResultBase {}
class Result : ResultBase {}

Task<ResultBase> GetResult() {
    return Task.FromResult(new Result());
}
//
///将输入任务的结果类型强制转换为协变
/// 
///任务的原始结果类型
///要返回的协变类型
///要投射的目标任务
[MethodImpl(MethodImplOptions.AggressiveInline)]
公共静态异步任务AsTask(此任务)
其中T:TResult
结果:在哪里上课
{
返回等待任务;
}
这样你就可以做到:

/// <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{}
类结果:ResultBase{}
任务GetResultAsync()=>…;//返回结果的异步代码
任务GetResultBaseAync()
{
返回GetResultAsync().AsTask();
}

在我的例子中,我在编译时不知道任务泛型参数,必须使用System.Threading.Tasks.Task基类。下面是我根据上面的例子创建的解决方案,可能会对某些人有所帮助

class ResultBase {}
class Result : ResultBase {}

Task<Result> GetResultAsync() => ...; // Some async code that returns Result

Task<ResultBase> GetResultBaseAsync() 
{
    return GetResultAsync().AsTask<Result, ResultBase>();
}
[MethodImpl(MethodImplOptions.AggressiveInline)]
公共静态异步任务AsTask(此任务)
{
var taskType=task.GetType();
等待任务;
返回(T)taskType.GetProperty(“结果”).GetValue(任务);
}

我在这方面取得了成功。在这一点上,它非常稳定(几年内没有更新),但安装起来很简单,要将ITask转换为任务,只需调用
.AsTask()
(包中还附带反向扩展方法)。

接口只能是协变或逆变的。类总是不变的。阅读更多内容:类在C#中是不变的。从这一点上看,似乎有人为它编写了代码。在本例中,您可以显式地提供类型参数:
Task.FromResult(new Result())
。它将编译。但是是的,
Task
是不变的,这是不好的。
async
await
依赖于一个合适的
GetAwaiter
方法的存在,因此它已经与
Task
类解耦。实际上
Task
Task
IAsyncEnumerable
IAsyncEnumerator
必须使
async
wait
c#中的代码。仅使用
GetAwaiter
方法的类是不够的。至少,编译器是这么说的。@FranciscoNeto,那根本不是真的
async
/
await
仅依赖于
GetAwaiter
方法的存在,该方法返回具有
IsCompleted
OnCompleted
GetResult
成员并具有适当签名的对象。因此,可等待项的自定义实现是可能的。来自哪个名称空间?@BennyM
Task
ContinueWith
方法位于
System.Threading.Tasks
名称空间中,而
MethodImpl
属性位于
System.Runtime.CompilerServices
中。另外一些建议:如果您只想向下转换,如示例中所示,这同样有效:
Task.FromResult(newresult())事实上,后一个注释应该是可以接受的答案,因为整个问题不是关于协方差,而是框架已经支持的情况。此代码正在创建额外的异步状态机,这将对性能产生影响。这仍然不是理想的解决方案。我想知道是否有理想的方法。如果您在编译时不知道类型,那么就不应该使用泛型。此外,此函数的执行速度非常慢,因为您正在使用反射-如果运行时
Task
不是
Task
,它将失败。@Dai,是的,它只是一个原型,应该进行类型检查
    [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);
    }