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
成员并具有适当签名的对象。因此,可等待项的自定义实现是可能的。来自哪个名称空间?@BennyMTask
和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);
}