T必须是违反有效的

Edu*_*rdo 34 .net c# generics covariance contravariance

这有什么问题?

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

它说:

方差无效:类型参数"T"必须在"MyNamespace.IRepository.Delete(T)"上具有矛盾的有效性.'T'是协变的.

Eri*_*ert 60

考虑如果编译器允许的话会发生什么:

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
Run Code Online (Sandbox Code Playgroud)

你只是试着在鱼上长出头发.

"out"表示"T仅用于输出位置".您正在输入位置使用它.

  • 我无法理解为什么在解释某些东西时,`T`和`IR`以及`C`和`x`是有效的变量名.这也适用于MSDN文档,尤其是泛型.什么是"D"? (12认同)
  • @David:它们是有效的,因为它们符合C#规范中的标识符标准,但我认为你的意思是教学上有效.使用短名称的教学法是巧妙地提醒读者,这是一个广泛适用的例子,他们应该在抽象中思考,而不是特定领域中特定问题的解决方案. (8认同)
  • 在教学上不那么有效......当例子让人想起概念而符号带来远离概念化的意义时。但它离方差还很远。 (2认同)
  • 我希望我在第一次尝试了解协方差时看到了这种解释:“'输出'表示'T仅用于输出位置。您正在输入位置使用它。” 我只是假定“ in”和“ out”是任意重复使用的关键字。 (2认同)
  • @AndrewKeeton:我们经历了漫长的设计过程,考虑了多种选择。与所有设计过程一样,有许多相互竞争的因素必须相互权衡。请参阅我 2007 年关于该主题的文章,了解一些选项及其优缺点:https://blogs.msdn.microsoft.com/ericlippert/2007/10/31/covariance-and-contravariance-in-c-part-八个语法选项/ (2认同)

Hei*_*nzi 42

您只能共同使用out类型参数,即返回类型.因此,IQueryable<T> GetAll()是正确的,但void Delete(T t)不是.

既然T在你的班级中同时使用和不相符,你就不能out在这里使用(也不是in).

如果您想进一步了解这背后的理论背景,请快速阅读并阅读"Covariance and Contravariance"维基百科文章.


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

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);
}
Run Code Online (Sandbox Code Playgroud)

这也是.NET BCL如何解决这个问题:IEnumerable<out T>是协变的,但只支持"读操作".ICollection<T>是一个子类型IEnumerable<out T>,允许读写操作,因此,本身不能协变.

  • 请注意,**可以*在参数中,但只能在"Action <T>"之类的内容中再次反转方向. (12认同)

Dar*_*rov 21

以下两种方法都是错误的:

void Save(T t);
void Delete(T t);
Run Code Online (Sandbox Code Playgroud)

你不能拥有T方法参数.仅作为返回类型,如果您希望它out T在通用定义中是covariant().

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

interface IRepository<in T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}
Run Code Online (Sandbox Code Playgroud)