Covariance and Contravariance in Generics

source: Covariance and Contravariance in Generics

Covariant type parameters

The following example illustrates covariant type parameters. The example defines two types: Base has a static method named PrintBases that takes anIEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) and prints the elements. Derived inherits from Base. The example creates an emptyList<Derived> (List(Of Derived) in Visual Basic) and demonstrates that this type can be passed to PrintBases and assigned to a variable of typeIEnumerable<Base> without casting. List implements IEnumerable, which has a single covariant type parameter. The covariant type parameter is the reason why an instance of IEnumerable<Derived> can be used instead of IEnumerable<Base>.

using System;
using System.Collections.Generic;

class Base
{
    public static void PrintBases(IEnumerable<Base> bases)
    {
        foreach(Base b in bases)
        {
            Console.WriteLine(b);
        }
    }
}

class Derived : Base
{
    public static void Main()
    {
        List<Derived> dlist = new List<Derived>();

        Derived.PrintBases(dlist);
        IEnumerable<Base> bIEnum = dlist;
    }
}

Contravariant type parameters

The following example illustrates contravariant type parameters. The example defines an abstract (MustInherit in Visual Basic) Shape class with an Areaproperty. The example also defines a ShapeAreaComparer class that implements IComparer<Shape> (IComparer(Of Shape) in Visual Basic). The implementation of the IComparerCompare method is based on the value of the Area property, so ShapeAreaComparer can be used to sort Shapeobjects by area.

The Circle class inherits Shape and overrides Area. The example creates a SortedSet of Circle objects, using a constructor that takes anIComparer<Circle> (IComparer(Of Circle) in Visual Basic). However, instead of passing an IComparer<Circle>, the example passes aShapeAreaComparer object, which implements IComparer<Shape>. The example can pass a comparer of a less derived type (Shape) when the code calls for a comparer of a more derived type (Circle), because the type parameter of the IComparer generic interface is contravariant.

When a new Circle object is added to the SortedSet<Circle>, the IComparer<Shape>.Compare method (IComparer(Of Shape).Comparemethod in Visual Basic) of the ShapeAreaComparer object is called each time the new element is compared to an existing element. The parameter type of the method (Shape) is less derived than the type that is being passed (Circle), so the call is type safe. Contravariance enables ShapeAreaComparerto sort a collection of any single type, as well as a mixed collection of types, that derive from Shape.

using System;
using System.Collections.Generic;

abstract class Shape
{
    public virtual double Area { get { return 0; }}
}

class Circle : Shape
{
    private double r;
    public Circle(double radius) { r = radius; }
    public double Radius { get { return r; }}
    public override double Area { get { return Math.PI * r * r; }}
}

class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>
{
    int IComparer<Shape>.Compare(Shape a, Shape b) 
    { 
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : a.Area.CompareTo(b.Area);
    }
}

class Program
{
    static void Main()
    {
        // You can pass ShapeAreaComparer, which implements IComparer<Shape>, 
        // even though the constructor for SortedSet<Circle> expects  
        // IComparer<Circle>, because type parameter T of IComparer<T> is 
        // contravariant.
        SortedSet<Circle> circlesByArea = 
            new SortedSet<Circle>(new ShapeAreaComparer()) 
                { new Circle(7.2), new Circle(100), null, new Circle(.01) };

        foreach (Circle c in circlesByArea)
        {
            Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);
        }
    }
}

/* This code example produces the following output:

null
Circle with area 0.000314159265358979
Circle with area 162.860163162095
Circle with area 31415.9265358979
 */
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s