Covariance, Contravariance and Invariance

来源:互联网 发布:java流程开发工具 编辑:程序博客网 时间:2024/05/18 06:21

Have a look at this article for an explanation of Covariance and Contravariance.

http://msdn.microsoft.com/en-us/library/dd799517.aspx


The CLR already had some support for variance in generic types and with c# 4 comes the syntax to use this. With generic variance the variance is applied to the type parameters of the interfaces and delegate types.

Covariance is about being able to treat a returned value as a more general type, and is possible when the interface method(s) only return that type. In this example the derived interface instance can be reassigned as the base, but not the other way around.

public interface ISomeInterfaceWithOut<out T> {     T GetSomething(); }  ISomeInterfaceWithOut<Apple> b = new Blah<Apple>(); ISomeInterfaceWithOut<Fruit> fruit = b; 

 

Contravariance is about being able to treat a parameter type as a more specific type, and is possible when the interface method(s) only consume that type. In this example the base interface instance can be reassigned as the derived, but not the other way around.

public interface ISomeInterfaceWithIn<in T> {     void SetSomething(T instance); }  ISomeInterfaceWithIn<Fruit> b = new Blah<Fruit>(); ISomeInterfaceWithIn<Apple> apple = b; 

 

Invariance is when the both cases are happening and the interface method(s) are both returning and consuming the type. Neither covariance or contravariance can apply. Here any usage like the above will not work, as the 'out T' covariance or 'in T' contravariance type parameter is not allowed to be defined as the methods contain both cases.

Consider this:

//it is not possible to declare 'out T' or 'in T' here - invalid variance public interface ISomeInterface<T> {     T GetSomething();     void SetSomething(T instance); } 

Neither of your examples will work as they are. Contravariance/covariance applies to interfaces and delegates which have had their generic type declared as 'in' / 'out', IList is invariant.

Since the IEnumerable<T> interface is covariant from .NET 4, you can do this from 4 onwards but not 3.5. Using fruits as IList here will not work when declaring fruits - it is not covariant.

List<Apple> apples = new List<Apple>(); //List<Apple> apples implements IEnumerable<Apple> IEnumerable<Fruit> fruits = apples; 

 

Here is the definition of IEnumerable<T>

//Version=4.0.0.0 public interface IEnumerable<out T> : IEnumerable {     new IEnumerator<T> GetEnumerator(); }  //Version=2.0.0.0 public interface IEnumerable<T> : IEnumerable {     new IEnumerator<T> GetEnumerator(); }