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;