Ronald Abellano
Ronald Abellano

Reputation: 874

IList<IList<int>> doesn't accept List<List<int>>?

List implements IList so I expect IList will accept a List object but why IList> doesn't accept List>?

static IList<int> List_1()
    {
        List<int> list = new List<int> { 1,2,3,3,4,5};

        return list;
    }

    static IList<IList<int>> List_2()
    {
        List<List<int>> parent = new List<List<int>>();
        List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
        parent.Add(list);

        return parent; //compiler error CS0266
    }

Upvotes: 5

Views: 3398

Answers (5)

er-sho
er-sho

Reputation: 9771

That's because of

List<T> implements IList<T> but

List<List<T>> does not implement IList<IList<int>>

That's why your first method works as intended and second not.

Just change your declaration of the list in the second method to

List<IList<int>> parent = new List<IList<int>>();

And this is the case of covariance and contravariance.

Generic type parameters support covariance and contravariance but you need to define in that way

By learn.microsoft.com

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types

Upvotes: 3

SᴇM
SᴇM

Reputation: 7213

I don't know why you want to return exactly IList<IList<int>>, but one way of doing that is to use Cast<T>() method:

static IList<IList<int>> List_2()
{
    List<List<int>> parent = new List<List<int>>();
    List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
    parent.Add(list);

    return parent.Cast<IList<int>>().ToList();
}

Or ConvertAll() method:

return parent.ConvertAll(x => (IList<int>)x);

Both methods will run over all elemets, and cast/convert them to a given type, so I think it would be better to return IList<List<int>> instead (if that's possible).

Upvotes: 0

Voodoo
Voodoo

Reputation: 1658

The problem is with your method return type. Modify your method signature to return to IList<List<int>> rather than returning IList<IList<int>>

static IList<List<int>> List_2()
    {
        List<List<int>> parent = new List<List<int>>();
        List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
        parent.Add(list);

        return parent; //no compiler error
    }

Now it will work fine as your method now returns an IList of List<int>

Upvotes: 0

Wiktor Zychla
Wiktor Zychla

Reputation: 48230

Suppose this works. Your client code is:

var result = List_2();

Since the contract allows adding to the result anything that's IList<int>, you could possibly have

public class MyCustomIList : IList<int>
{
    ...
}

and then

var result = List_2();
result.Add( new MyCustomIList() );

But that's wrong!

Your result is a list of List<int>, you should not be allowed to add anything other than List<int> or its derivatives there. However, you were able to add MyCustomIList which is not related to the List<int>.

If you need a broad picture of the issue, read more on covariance and contravariance.

The fundamental issue in this particular example comes from the Add operation. If you don't need it, the IEnumerable will do

static IEnumerable<IEnumerable<int>> List_2()
{
    List<List<int>> parent = new List<List<int>>();
    List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
    parent.Add(list);

    return parent; // no error, this works
}

This has been covered already.

Upvotes: 1

vsarunov
vsarunov

Reputation: 1547

Why then does List implement IList?

It is a bit odd, since List for any type other than object does not fulfill the full contract of IList. It's probably to make it easier on people who are updating old C# 1.0 code to use generics; those people were probably already ensuring that only the right types got into their lists. And most of the time when you're passing an IList around, it is so the callee can get by-index access to the list, not so that it can add new items of arbitrary type.

I would suggeste return IEnumerable instead of IList, will simplify your life, since List Fully implements it.

Upvotes: 0

Related Questions