T必须是违反有效的

时间:2011-02-18 13:11:00

标签: c# .net generics covariance contravariance

这有什么问题?

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

它说:

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

3 个答案:

答案 0 :(得分:58)

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

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仅用于输出位置”。您正在输入位置使用它。

答案 1 :(得分:41)

您只能共同使用out类型参数,即返回类型。因此,IQueryable<T> 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<out T>是协变的,但只支持“读操作”。 ICollection<T>IEnumerable<out T>的子类型,允许读写操作,因此本身不能协变。

答案 2 :(得分:21)

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

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