我真的很喜欢使用C#5.0异步编程。然而,在一些地方,更新旧代码以与TAP模型保持一致会给我带来问题
这里有一个-我不确定为什么Task<TResult>
在TResult中不是协变的,但当我试图更新协变接口以从同步模式移动到异步模式时,它给我带来了问题:
旧代码:
public interface IInitializable<out T> // ** out generic modifier **
{
/// <summary>
/// Boolean to indicate if class is ready
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Calls for instance to be initialized using current parameters
/// Driver initialization can be done in the default constructor if desired
/// </summary>
T Initialize();
}
新代码(不会编译):
public interface IAsyncInitializable<out T> // ** out generic modifier...broken **
{
/// <summary>
/// Boolean to indicate if class is ready
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Calls for instance to be initialized using current parameters
/// Driver initialization can be done in the default constructor if desired
/// </summary>
Task<T> InitializeAsync(); // ** breaks because Task<TResult> is invariant in TResult **
}
有没有一种合理的方法可以绕过这一点,而不会过于剧烈地修改我的API?(额外的好处:为什么任务不是协变的?)。没有IAwaitable接口,但我想我可以创建一个接口,并创建一个扩展方法,将其转换为包装的、协变的、可调整的任务对象。还是我做错了?
Task<T>
在T
中不能协变,因为它是一个类。只有接口和委托可以具有一般差异。
至于是否值得做包装。。。我想这取决于你在项目中使用协方差的程度。老实说,我怀疑随着时间的推移,你会发现所有的包装和展开都很混乱——如果只是去除协方差并不太糟糕,我会这么做的。
我认为,在ITask接口上不包括对async关键字的编译器支持是微软的一个主要疏忽。幸运的是,绕过这个限制并不太难。
我已经实现了一个协变的不适用的ITask<out TResult>
接口。它的用法非常简单。
更多信息请访问:
https://github.com/jam40jeff/ITask