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?
If, for instance, your clients want a concrete, tangible, and (generally) modifiable list of items, IList makes sense. If you want to return something with an ordering that matters and give them the ability to change that ordering, then give them a list.
Generally, it's better to return IEnumerable<T> , as long as that has everything the caller needs.
In C# IList interface is an interface that belongs to the collection module where we can access each element by index. Or we can say that it is a collection of objects that are used to access each element individually with the help of an index. It is of both generic and non-generic types.
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
).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With