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 of string to IEnumerable of object.

What is Covariance in C#?

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#.

Written by Loek van den Ouweland on 2017-02-17.
Questions regarding this artice? You can send them to the address below.