T must be contravariantly valid

白昼怎懂夜的黑 提交于 2019-11-26 14:05:54

问题


What is wrong with this?

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

It says:

Invalid variance: The type parameter 'T' must be contravariantly valid on 'MyNamespace.IRepository.Delete(T)'. 'T' is covariant.


回答1:


Consider what would happen if the compiler allowed that:

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

And you just tried to grow hair on a fish.

The "out" means "T is only used in output positions". You are using it in an input position.




回答2:


You can use an out type parameter only covariantly, i.e., in the return type. Therefore, IQueryable<T> GetAll() is correct, but void Delete(T t) is not.

Since T is used both co- and contravariantly in your class, you cannot use out here (nor in).

If you want to know more about the theoretical background behind this, take a quick break and read the "Covariance and Contravariance" Wikipedia article.


Welcome back. So, what do you do if you need all those methods in your repository but still need a covariant interface? You can extract the covariant part into its own interface:

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

This is also how the .NET BCL solves this issue: IEnumerable<out T> is covariant, but only supports "read operations". ICollection<T> is a subtype of IEnumerable<out T>, allows read and write operations, and, thus, cannot be covariant itself.




回答3:


The following two methods are wrong:

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

You can't have T as method argument. Only as return type if you want it to be covariant (out T) in your generic definition.

Or if you want contravariance then you could use the generic parameter only as method argument and not return type:

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


来源:https://stackoverflow.com/questions/5041664/t-must-be-contravariantly-valid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!