38

这有什么问题?

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

它说:

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

4

3 回答 3

67

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

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

于 2011-02-18T15:22:19.557 回答
44

您只能协变地使用out类型参数,即在返回类型中。因此,IQueryable<T> GetAll()是正确的,但void Delete(T t)不是。

由于T在您的课程中同时使用协变和逆变,因此您不能out在此处使用(也不in)。

如果您想了解更多关于这背后的理论背景,请稍事休息并阅读“协变和逆变”维基百科文章


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

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>,允许读取和写入操作,因此,它本身不能是协变的。

于 2011-02-18T13:13:23.053 回答
23

以下两种方法是错误的:

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

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

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

interface IRepository<in T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}
于 2011-02-18T13:12:49.910 回答