【问题标题】:T must be contravariantly validT 必须是逆变有效的
【发布时间】:2011-06-29 19:48:14
【问题描述】:

这有什么问题?

interface IRepository<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
    void Save(T t);
    void Delete(T t);
}

上面写着:

无效方差:类型参数“T”必须在“MyNamespace.IRepository.Delete(T)”上逆变有效。 'T' 是协变的。

【问题讨论】:

  • 你最后做了什么?我面临同样的问题。答案并不能真正解决它。我需要 GetAll、Save 和 Delete 在同一个类中
  • 对不起,我不记得了。那是 4 年前的事了。

标签: c# .net generics covariance contravariance


【解决方案1】:

您只能协变地使用 out 类型参数,即在返回类型中。因此,IQueryable&lt;T&gt; GetAll() 是正确的,但 void Delete(T t) 是错误的。

由于T 在您的类中同时使用,因此您不能在此处使用out(也不能使用in)。

如果您想了解更多关于这背后的理论背景,请稍事休息并阅读"Covariance and Contravariance" Wikipedia article


欢迎回来。那么,如果您需要存储库中的所有这些方法,但仍需要协变接口,您会怎么做?您可以将协变部分提取到自己的接口中:

interface IDataSource<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
}

interface IRepository<T> : IDataSource<T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}

这也是 .NET BCL 解决此问题的方式:IEnumerable&lt;out T&gt; 是协变的,但仅支持“读取操作”。 ICollection&lt;T&gt;IEnumerable&lt;out T&gt; 的子类型,允许读写操作,因此本身不能是协变的。

【讨论】:

  • 请注意,它可以在参数中,但只能使用Action&lt;T&gt;之类的东西,它会再次反转方向。
【解决方案2】:

考虑一下如果编译器允许这样做会发生什么:

interface IR<out T>
{
    void D(T t);
}

class C : IR<Mammal>
{
    public void D(Mammal m)
    {
        m.GrowHair();
    }
}
...
IR<Animal> x = new C(); 
// legal because T is covariant and Mammal is convertible to Animal
x.D(new Fish()); // legal because IR<Animal>.D takes an Animal

而你只是想在鱼身上长毛。

“out”表示“T 仅用于输出位置”。您在输入位置使用它。

【讨论】:

  • 我永远无法理解为什么在解释某事时,TIRCx 是有效的变量名。这也适用于 MSDN 文档,尤其是泛型。什么是“D”?
  • @David:它们是有效的,因为它们符合 C# 规范中的标识符标准,但我认为您的意思是在教学上有效。使用短名称的教学法是为了巧妙地提醒读者,这是一个普遍的、广泛适用的例子,他们应该抽象地思考,而不是针对特定领域的特定问题的解决方案。
  • 在教学上不是那么有效......当例子带来概念,而符号带来远非概念化的意义时。但它远非方差。
  • 我希望我在第一次尝试了解协/逆变时看到了这个解释:“'out' 意味着'T 仅用于输出位置'。您在输入位置使用它。 "我刚刚假设inout 是任意重复使用的关键字。
  • @AndrewKeeton:我们经历了一个漫长的设计过程,我们考虑了许多选项。像所有设计过程一样,有许多相互竞争的因素必须相互权衡。有关一些选项及其优缺点,请参阅我 2007 年关于该主题的文章:blogs.msdn.microsoft.com/ericlippert/2007/10/31/…
【解决方案3】:

以下两种方法错误:

void Save(T t);
void Delete(T t);

您不能将T 作为方法参数。如果您希望它在您的泛型定义中是协变的 (out T),则仅作为返回类型。

或者如果你想要逆变,那么你可以只使用泛型参数作为方法参数而不是返回类型:

interface IRepository<in T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}

【讨论】:

  • 完整而干净的答案。谢谢
  • 现在我终于明白为什么关键字是inout。类的输入和类的输出。这已经有一段时间了,谢谢。
猜你喜欢
  • 1970-01-01
  • 2022-08-05
  • 2020-08-29
  • 2021-07-30
  • 2021-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多