What is Covariance in C#?

Have you ever wondered why the C# compiler does not complain when you feed lists of strings into a function that expects a list of objects? Why does that work out of the box? After reading this, you understand how Covariance helps us to convert IEnumerable<string> to IEnumerable<object>.

Suppose we have a list of strings

var s = new List<string>();

and a function that takes a list of objects

void Test(IEnumerable<object> input)

Without Covariance the compiler would give us a build error like:

Cannot convert List<string> to IEnumerable<object>

But C# (4.0 and up) has Covariance that allows passing lists of subtypes.

Because strings derive from (and therefore are) objects, covariance in C# allows conversion of a list of strings to a list of objects.

Now we know why it works. But how does it work?

If you go to the definition of IEnumerable<T>, you see the out keyword:

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

The trick is in the out keyword. This is called a generic modifier and should not be confused with out-parameters. Perhaps you asked yourself the question: “How does the generic List implementation perform the cast to another type?”. It does not. The out keyword makes the compiler do this for us.

Here is a console app example so you can test it yourself:

// covariance is used to convert IEnumerable<string> to IEnumerable<object>. This works assuming that:
// 1) The generic IEnumerable interface is marked with the 'out' keyword (IEnumerable<out T>). Which is true in C#.
// 2) string derives from (and therefore is) an object.
class Program
{
    static void Main(string[] args)
    {
        var s = new List<string>();

        // it is allowed to pass a list of strings when a list of objects is expected because the compiler 
        // uses covariance to convert ienumerable<string> to ienumerable<object>   
        Test(s); 
    }

    static void Test(IEnumerable<object> input)
    {
        foreach (var item in input)
        {
        }
    }
}

Here’s a list of Covariant interfaces in C#.