Search code examples
c#listgenericscovariance

Why can't I return a List<List<T>> when the return type is IList<IList<T>> in C#?


I'm trying to return a List<List<string>> in a method that has a return type of IList<IList<string>>:

IList<IList<string>> Foo()
{
    return new List<List<string>>();
}

However, I'm getting a compile error that says I can't convert List<List<string>> to IList<IList<string>>. I thought that since List<T> implements IList<T>, I should be able to return a List<List<string>> where an IList<IList<string>> is expected.

I've asked copilot about it and got answers about IList not being covariant or contravariant but I still can't wrap my mind around it. Return type of List<IList<string>> is valid and it confuses me.


Solution

  • Generic interface covariance means that we can substitute for T a type that is the same or assignable to T (in case of class hierarchy more derived, in case of interfaces - a class that implements the interface)

    IList<T> is not covariant. IEnumerable<out T> is (notice the out keyword)

    For example, this would work fine with IEnumerable because we can substitute the generic parameter IList<string> with List<string> because the latter implements the IList<string> interface.

    IEnumerable<IList<string>> Foo() {
        return new List<List<string>>();
    }
    

    However, with IList<T> you need to substitute for the exact same T.

    So, if you need to stick to your example you can modify it to do this:

    IList<IList<string>> Foo() {
        return new List<IList<string>>();
    }
    

    and use it like this without issues

    var foo = Foo();
    foo.Add(new List<string>());
    foo[0].Add("bar");
    Console.WriteLine(foo[0][0]);