11

我真的很喜欢使用 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?(奖励:为什么 Task 不是协变的?)。没有 IAwaitable 接口,但我想我可以制作一个并创建一个扩展方法,该方法转换为一个包装好的、协变的、可等待的任务对象。还是我做错了?

4

2 回答 2

14

Task<T>不能是协变的T,因为它是一个类。只有接口和委托可以具有通用方差。

至于是否值得进行包装......我想这取决于您在项目中使用协方差的程度。老实说,我怀疑随着时间的推移,您会发​​现所有包装和展开令人困惑 - 如果只是消除协方差的影响还不错,我会这样做。

于 2012-08-30T20:18:45.307 回答
7

我相信在 ITask 接口上不包括对 async 关键字的编译器支持是微软的主要疏忽。幸运的是,绕过这个限制并不难。

我已经实现了一个协变的等待ITask<out TResult>接口。它的用法非常简单。

更多信息可以在以下位置找到:

https://github.com/jam40jeff/ITask

于 2014-10-15T22:07:24.567 回答