问题
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