Returning IList<IList<T>>
Asked Answered
D

2

14

I have a method which builds lists of lists. I'd like to have the return type use the generic IList<> interface to reduce coupling with the concrete List<> type downstream. However, the compiler struggles with the type conversion.

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

Why does this fail when this works:

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

What's the most elegant way out of this mess?

Doridoria answered 15/11, 2011 at 20:0 Comment(1)
What version of .NET are you using?Xanthin
P
23

Just do this:

return new List<IList<T>>();

Because List<T> implements IList<T>, you will be able to add any kind of IList<T> to the result. For example:

Foo<int>().Add(new List<int>());

As to the why, this is a matter of covariance/contravariance (I always get the two mixed up). Basically, if you say you're returning an IList<IList<T>>, then someone should be able to add any kind of IList<T> to the result:

Foo<int>().Add(new []{1}); // arrays implement `IList`

Obviously if you had returned a List<List<T>> then the above line of code wouldn't work: you would be attempting to add a T[] to the collection of List<T>. So even though a List<T> is an IList<T>, a List<List<T>> is not an IList<IList<T>>.

Note: This next portion only applies to .NET 4 and up, as that was the first version to support covariance and contravariance in interfaces.

On the other hand, if you know that the person consuming your result isn't planning to add anything to the list, but is only trying to iterate across it, you could change your return type to IEnumerable<IList<T>> and then it would be perfectly fine to return a List<List<T>>. Why? Because the IEnumerable<T> interface is covariant:

public interface IEnumerable<out T>

That "out" tells the compiler that this interface will only have methods that return values related to T (like GetEnumerator), and it won't have any methods that take something related to T as a parameter (like Add).

Piquet answered 15/11, 2011 at 20:3 Comment(2)
Minor quibble: LinkedList<> does not implement IList<>.Place
Thanks for the tip. I'm now building an IEnumerable<IList<T>> and using Linq .ToList() for the return.Mistrial
F
1

That is related to C# covariance and contravariance. You can read more here.

To do interface of interface of T, your interfaces must both be marked as either out T or in T. IEnumerable is marked as out T.

Fibered answered 15/11, 2011 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.