Covariance and Contra-variance

Previous to .NET 4, generic interfaces were invariant. .NET 4 adds an important extension for generic
interfaces and generic delegates with covariance and contra-variance. Covariance and contra-variance
are about the conversion of types with argument and return types. For example, can you pass a
Rectangle to a method that requests a Shape? Let’s get into examples to see the advantages of these
With .NET, parameter types are covariant. Assume you have the classes Shape and Rectangle, and
Rectangle derives from the Shape base class. The Display() method is declared to accept an object of the
Shape type as its parameter:
public void Display(Shape o) { }
Now you can pass any object that derives from the Shape base class. Because Rectangle derives
from Shape, a Rectangle fulfills all the requirements of a Shape and the compiler accepts this

method call:

Rectangle r = new Rectangle { Width= 5, Height=2.5};


Return types of methods are contra - variant. When a method returns a Shape it is not possible to assign it to
a Rectangle because a Shape is not necessarily always a Rectangle . The opposite is possible. If a method
returns a Rectangle as the GetRectangle() method,
public Rectangle GetRectangle();
the result can be assigned to a Shape .
Shape s = GetRectangle();
Before version 4 of the .NET Framework, this behavior was not possible with generics. With C# 4, the
language is extended to support covariance and contra - variance with generic interfaces and generic
delegates. Let ’ s start by defi ning a Shape base class and a Rectangle class:

public class Shape
public double Width { get; set; }
public double Height { get; set; }
public override string ToString()
return String.Format("Width: {0}, Height: {1}", Width, Height);

public class Rectangle: Shape

Covariance with generic interfaces
A generic interface is covariant if the generic type is annotated with the out keyword. This also means that
type T is allowed only with return types. The interface IIndex is covariant with type T and returns this type
from a read - only indexer:
public interface IIndex < out T >
T this[int index] { get; }
int Count { get; }

The RectangleCollection.GetRectangles() method returns a RectangleCollection that implements
the IIndex<Rectangle> interface, so you can assign the return value to a variable rectangle of the
IIndex<Rectangle> type. Because the interface is covariant, it is also possible to assign the returned value
to a variable of IIndex<Shape>. Shape does not need anything more than a Rectangle has to offer. Using
the shapes variable, the indexer from the interface and the Count property are used within the for loop:
static void Main()
IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles();
IIndex<Shape> shapes = rectangles;
for (int i = 0; i < shapes.Count; i++)

Contra-Variance with generic interfaces
A generic interface is contra-variant if the generic type is annotated with the in keyword. This way the
interface is only allowed to use generic type T as input to its methods:

public interface IDisplay<in T>
void Show(T item);

The ShapeDisplay class implements IDisplay<Shape> and uses a Shape object as an input parameter:
public class ShapeDisplay: IDisplay<Shape>
public void Show(Shape s)
Console.WriteLine("{0} Width: {1}, Height: {2}", s.GetType().Name,
s.Width, s.Height);

Creating a new instance of ShapeDisplay returns IDisplay<Shape>, which is assigned to the
shapeDisplay variable. Because IDisplay<T> is contra-variant, it is possible to assign the result to
IDisplay<Rectangle> where Rectangle derives from Shape. This time the methods of the interface only
define the generic type as input, and Rectangle fulfills all the requirements of a Shape:
static void Main()
IDisplay<Shape> shapeDisplay = new ShapeDisplay();
IDisplay<Rectangle> rectangleDisplay = shapeDisplay;

个人分类: C#
想对作者说点什么? 我来说一句