【问题标题】:Can/should Task<TResult> be wrapped in a C# 5.0 awaitable which is covariant in TResult?可以/应该将 Task<TResult> 包装在 TResult 中协变的 C# 5.0 awaitable 中吗?
【发布时间】:2012-08-25 15:30:58
【问题描述】:

我真的很喜欢使用 C# 5.0 异步编程。但是,在某些地方更新旧代码以与 TAP 模型保持一致会给我带来问题。

这是其中之一 - 我不确定为什么 Task&lt;TResult&gt; 在 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 接口,但我想我可以制作一个并创建一个扩展方法,该方法转换为一个包装好的、协变的、可等待的任务对象。还是我做错了?

【问题讨论】:

  • 顺便说一句,即使Task 是协变接口,您的代码也无法编译。正确的版本是Task&lt;T&gt; InitializeAsync();(该行没有out 修饰符)。
  • 一个很好的观点。我在 VS 中更正了这一点,但忘记编辑 SO。

标签: c# task-parallel-library covariance async-await c#-5.0


【解决方案1】:

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

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

更多信息请访问:

https://github.com/jam40jeff/ITask

【讨论】:

  • 确实做得很好。在您的库中使用通用异步返回类型具有很高的教育意义。
【解决方案2】:

Task&lt;T&gt;T 中不能是协变的,因为它是一个类。只有接口和委托可以有泛型变量。

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

【讨论】:

  • 感谢您的建议!我使用您的 Eduasync TaskAwaiter(T) 实现了 IAwaitable(out TResult),并且可以看到使用单独的接口绝对不值得失去可读性/可用性。而且,很重要的一点是:类和通用方差...很荣幸@Jon Skeet 指出了我的错误:)。但是,没有 ITask(out T) 是一个无赖。我在这里看到了 Stephen Cleary 的评论 (stackoverflow.com/questions/8407227/…),但作为使用具体类型的理由,WinRT 互操作兼容性有点不令人满意。
  • @DavidCuccia:您应该阅读 Stephen Toub 的博客,了解关于等待者的传播上下文。它比 Eduasync 节目有点棘手:(
  • @JonSkeet - 知道为什么他们的框架开发人员没有添加ITask&lt;T&gt; 而是只实现了一个类吗?
  • @PhilipPittle:一方面,Task&lt;T&gt; 出现在 async/await 之前——它主要是为并行而设计的。如果现在从头开始设计,可能会有很多不同的东西。
猜你喜欢
  • 1970-01-01
  • 2022-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-30
相关资源
最近更新 更多