任务的协方差和逆变

lok*_*ing 9 c# task covariance contravariance async-await

鉴于以下片段,我完全不明白为什么即将实现的目标是不可能的:

接口:

public interface IEntityRepository<out T> : IRepository<IEntity> {

    void RequeryDataBase();

    IEnumerable<T> Search(string pattern);

    Task<IEnumerable<T>> SearchAsync(string pattern);

    SearchContext Context { get; }

    string BaseTableName { get; }
  }
Run Code Online (Sandbox Code Playgroud)

In IRepository<IEntity>只是简单的通用CRUD定义.

我在这一行得到错误: Task<IEnumerable<T>> SearchAsync(string pattern);

错误:

方法返回类型必须输出安全.无效方差:类型参数T必须在Task上无效

请帮我明白了,为什么我不能使用<out T>Task<T>

Cha*_*ger 11

Task<T>是不协变的.方差只能应用于通用接口(和委托,但这里不相关).

例如Task<IEnumerable<Dog>> ,不能分配给Task<IEnumerable<Animal>>.因此,您的界面也不能标记为协变.

您可能希望看到此相关问题.

  • 他们可以提供他们想要的所有解释.在引入`Task <T>`,`ITask`但没有'ITask <out T>`时,我仍然觉得有人做错了. (6认同)

Zor*_*vat 5

在某些通用接口中确定泛型类型参数的方差时,必须考虑接口内泛型类型参数的所有用法.每次使用都可能引入一些关于方差的约束.这包括:

  • 用作方法的输入参数 - 不允许协方差
  • 用作方法的返回值 - 不允许相反
  • 用作其他泛型推导的一部分,例如Task<T>- 此类使用可能不允许协方差,不允许相反,或两者兼而有之

我使用负逻辑强调所有这些情况基本上都是引入约束.在分析之后,您将知道是否有任何元素不允许协方差,然后您的参数可能不会被声明为out.相反,如果任何元素不允许相反,则您的参数可能不会被声明为in.

在您的特定情况下,第Search一种方法返回IEnumerable<T>,使逆转不适用.但是,SearchAsync正在返回Task<IEnumerable<T>>,并且该用法引入了Task类型中存在的约束 - 它是不变的,这意味着此时out也是不可能的.

结果是您的通用接口必须在其通用参数类型上保持不变,以满足其所有方法的签名.

  • 我不完全理解为什么没有 ITask&lt;out T&gt; 接口。这将有助于解决这些问题。 (2认同)