Mark Needham

Thoughts on Software Development

C#: A lack of covariance with generics example

with 9 comments

One of the things I find most confusing when reading about programming languages is the idea of covariance and contravariance and while I’ve previously read that covariance is not possible when using generics in C# I recently came across an example where I saw that this was true.

I came across this problem while looking at how to refactor some code which has been written in an imperative style:

public interface IFoo 
{
	string Bar { get; set; } 
}
 
public class Foo : IFoo 
{ 
	public string Bar { get; set; }
}
private IEnumerable<IFoo> GetMeFoos()
{
    var someStrings = new[] { "mike", "mark" };
 
    var someFoos = new List<IFoo>();
    foreach (var s in someStrings)
    {
        someFoos.Add(new Foo { Bar = s });
    }
    return someFoos;
}

I changed the code to read like so:

private IEnumerable<IFoo> GetMeFoos()
{
    var someStrings = new[] { "mike", "mark" };
    return someStrings.Select(s => new Foo { Bar = s });
}

Which fails with the following compilation error:

Error	1	Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Test.Foo>' to 'System.Collections.Generic.IEnumerable<Test.IFoo>'. An explicit conversion exists (are you missing a cast?)

I thought the compiler would infer that I actually wanted a collection of ‘IFoo’ given that I was returning from the method directly after the call to Select but it doesn’t.

As I understand it the reason that we can’t downcast an IEnumerable of ‘Foo’ to an IEnumberable of ‘IFoo’ is that we would run into problems if we worked of the assumption that our original collection only contained Foos in it later on in our program.

For example it would be possible to add any item which implemented the ‘IFoo’ interface into the collection even if it wasn’t a ‘Foo’:

// this code won't compile
 
List<Foo> foos = new List<Foo>();
// add some foos
 
List<IFoo> ifoos = foos;
 
foos.Add(new SomeOtherTypeThatImplementsIFoo());

It’s not possible to convert ‘SomeOtherTypeThatImplementsIFoo’ to ‘Foo’ so we would run ourself into problems.

Rick Byers has a post from a few years ago where he explains how this works in more detail and also points out that covariance of generics is actually supported by the CLR, just not by C#.

In the case I described we can get around the problem by casting ‘Foo’ to ‘IFoo’ inside the ‘Select’:

private IEnumerable<IFoo> GetMeFoos()
{
    var someStrings = new[] { "mike", "mark" };
    return someStrings.Select(s => (IFoo) new Foo { Bar = s });
}

Written by Mark Needham

February 20th, 2010 at 12:17 pm

Posted in .NET

Tagged with ,

  • http://www.dump.hr Ivan Maček
  • http://www.markhneedham.com Mark Needham

    Ah cool, I’d read a bit about how it was going to be in C# 4.0 but I really like the explanations used in that post, thanks!

  • Kalpesh

    A small correction, in the code above

    public class Foo : IFoo
    {
    public string Bar { get; set; }
    }

    The property name is missing above.
    Hope that helps.

  • http://www.markhneedham.com Mark Needham

    @Kalpesh – good spot, have edited that in there now.

  • http://blog.functionalfun.net Samuel Jack

    You can also use the Cast() extension method, like this:

    return someStrings.Select(s => new Foo { Bar = s }).Cast();

  • http://blog.functionalfun.net Samuel Jack

    Oops, the browser swallowed my angle brackets! Try again:

    return someStrings.Select(s => new Foo { Bar = s }).Cast<IFoo>();

  • http://www.the-arm.com/ toni

    Remember me to show you the code I wrote to avoid this Monday!

  • http://cairey.wordpress.com/ Chris Airey

    I was going to say upcast explicitly using Cast() as well. This is required for the collection of IFoos.

  • http://cairey.wordpress.com/ Chris Airey

    Opps… I mean Cast()