代码之家  ›  专栏  ›  技术社区  ›  lokusking

任务的协方差和反方差

  •  13
  • lokusking  · 技术社区  · 8 年前

    鉴于以下片段,我完全不理解 为什么? 我要实现的目标是不可能的:

    接口:

    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; }
      }
    

    在里面 IRepository<IEntity> 只是简单的泛型 原油 定义

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

    错误:

    方法返回类型必须是输出安全的。方差无效:类型参数T在Task上必须始终有效

    请帮助我理解,为什么我不能使用 <out T> 用一个 Task<T>

    5 回复  |  直到 8 年前
        1
  •  18
  •   Community CDub    8 年前

    Task<T> 不是协变的。差异只能应用于通用 接口

    例如 Task<IEnumerable<Dog>> 不能 被分配给 Task<IEnumerable<Animal>> 。因此,您的接口也不能标记为协变。

    你可能想看看 this related question .

        2
  •  6
  •   Zoran Horvat    8 年前

    在决定某个泛型接口中泛型类型参数的方差时,必须考虑接口内泛型类型变量的所有使用。每次使用可能会引入一些关于方差的约束。这包括:

    • 用作方法的输入参数-不允许协方差
    • 用作方法的返回值-不允许矛盾
    • 用作其他泛型派生的一部分,例如 Task<T> -这种用法可能不允许协方差,不允许反方差,或两者兼而有之

    我用否定的逻辑来强调,所有这些案例基本上都引入了限制。分析之后,您将知道是否有任何元素具有不允许的协方差,然后您的参数可能不会声明为 out in .

    Search 方法返回 IEnumerable<T> ,导致逆差不适用。但是,SearchAsync正在返回 Task<IEnumerable<T>> ,并且该用法引入了Task类型中存在的约束-它是不变的,这意味着这一次 出来 也是不可能的。

    结果是,泛型接口必须在泛型参数类型上保持不变,才能满足其所有方法的签名。

        3
  •  1
  •   Theodor Zoulias    3 年前

    IAsyncEnumerable<T> 而不是 Task<IEnumerable<T>> .

    协变接口如下所示:

    public interface IEntityRepository<out T> : IRepository<IEntity> {
    
        void RequeryDataBase();
    
        IEnumerable<T> Search(string pattern);
    
        IAsyncEnumerable<T> SearchAsync(string pattern);
    
        SearchContext Context { get; }
    
        string BaseTableName { get; }
      }
    

    here .

        4
  •  0
  •   IluTov    7 年前

    正如其他人之前提到的那样, TResult 在里面 Task<TResult> 不是协变的。不过,您可以创建一个新的 Task 实例使用 ContinueWith :

    var intTask = Task.FromResult(42);
    var objectTask = intTask.ContinueWith(t => (object) t.Result);
    
    await objectTask; // 42
    
        5
  •  -1
  •   IluTov    7 年前

    你可以欺骗和使用

    IObservable<out T>
    

    哪个是 几乎 与…一样 Task<T> 任务<T> 你只得到一个结果,但是 IObservable<T> 你可以得到很多。但是你可以做到

    IObservable<T> o = ...
    var foo = await o;
    

    就跟

    Task<T> t = ...
    var foo = await t;
    

    System.Reactive.Linq 命名空间 Rx library 才能使这项工作成功。它将向添加一个扩展方法 IObservable<> 这将使它变得可行。