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