#1,067 – Covariance and Generic Interfaces
April 3, 2014 1 Comment
Generic interfaces in C# are covariant, provided that their type parameters are constrained with the out keyword.
Let’s assume that we have the following interface.
public interface FirstAndLast<T> { T First(); T Last(); }
Then assume that we define a generic class that implements this interface:
public class LineOf<T> : IFirstAndLast<T> { private Queue<T> theQueue = new Queue<T>(); public void AddToEnd(T d) { theQueue.Enqueue(d); } public void RemoveFromFront(T d) { theQueue.Dequeue(); } public T First() { return theQueue.Peek(); } public T Last() { return theQueue.Last(); } }
Great, now we can use this class as follows:
LineOf<Dog> dogs = new LineOf<Dog>(); dogs.AddToEnd(new Dog("Lassie")); dogs.AddToEnd(new Dog("Rin Tin Tin")); Console.WriteLine(dogs.First().ToString() + ", " + dogs.Last().ToString()); LineOf<BorderCollie> herders = new LineOf<BorderCollie>(); herders.AddToEnd(new BorderCollie("Kirby")); herders.AddToEnd(new BorderCollie("Shep")); Console.WriteLine(herders.First().ToString() + ", " + herders.Last().ToString());
At this point, we might want to convert LineOf<BorderCollie> to IFirstAndLast<Dog>, for example we might have a method that returns IFirstAndLast<Dog>.
However, if we do the following, we get a compiler error, saying that we can’t implicitly cast LineOf<BorderCollie> to IFirstAndLast<Dog>:
IFirstAndLast<Dog> dogLine = herders;
We could do a cast, as shown below. The code now compiles, but the conversion fails at runtime.
IFirstAndLast<Dog> dogLine = (IFirstAndLast<Dog>)herders;
We want the IFirstAndLast<T> interface to be covariant, i.e. to allow this assignment. To support this, we just need to add the out keyword in the interface.
public interface IFirstAndLast<out T>
We can do this because T is only used in this interface as a return value. Having done this, we can now do the following. This compiles and the assignment succeeds at runtime.
IFirstAndLast<Dog> dogLine = herders;