IList<IList<int>>不接受List<List<int>>?

4
List 实现了 IList 接口,所以我期望 IList 将接受 List 对象, 但是为什么 IList<T> 不接受 List<T> 呢?
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
    }

2
你明白为什么 List<object> 不接受 List<string> 吗? - Sweeper
5
返回(IList <IList <int>>)parent; - jdweng
2
@jdweng:那样做不行,你会得到一个运行时异常。 - Wiktor Zychla
1
@SᴇM 这不是关于将一种类型转换为对象类型,而是关于 IList 和 List。 - vsarunov
@vsarunov 是的,因为 MyClass 派生自 object,就像 List<T> 派生自 IList<T> 一样。所以如果你有一个返回 List<object> 的方法,你不能返回 List<MyClass>,除非进行强制转换。 - SᴇM
显示剩余4条评论
5个回答

3
这是因为:
List 实现了 IList 但是
List> 却没有实现 IList> 这就是为什么你的第一个方法按预期工作而第二个方法不起作用的原因。
只需更改第二种方法中列表的声明即可。
List<IList<int>> parent = new List<IList<int>>();

这是协变性和逆变性的情况。
泛型类型参数支持协变性和逆变性,但需要以这种方式定义。
通过 learn.microsoft.com 协变性和逆变性是指能够使用比原先指定的更派生类型(更具体)或更不派生类型(更不具体)。泛型类型参数支持协变性和逆变性,以提供在分配和使用泛型类型时更大的灵活性。

1
假设这个有效。您的客户端代码是:

var result = List_2();

由于合同允许将任何 IList<int> 添加到结果中,因此您可能会有

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

然后

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

但是那是错误的!

您的result是一个List<int>的列表,您不应该添加除List<int>或其派生类以外的任何内容。但是,您却能够添加与List<int>无关的MyCustomIList

如果您需要了解这个问题的整体情况,请阅读更多关于协变和逆变的内容。

在这个特定示例中的根本问题来自于Add操作。如果您不需要它,那么IEnumerable就足够了。

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
}

这已经被涵盖了。

0
为什么 List 实现了 IList 呢?
这有点奇怪,因为对于除 object 以外的任何类型,List 都无法完全履行 IList 的全部契约。这可能是为了方便那些正在更新旧的 C# 1.0 代码以使用泛型的人们;这些人可能已经确保只有正确的类型进入他们的列表中。当你传递一个 IList 时,大多数情况下是为了调用者可以通过索引访问列表,而不是为了添加任意类型的新项。
我建议返回 IEnumerable 而不是 IList,这样会简化你的生活,因为 List 完全实现了它。

0

我不知道为什么你想要返回 IList<IList<int>>,但是一个方法就是使用 Cast<T>() 方法

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();
}

或者ConvertAll()方法
return parent.ConvertAll(x => (IList<int>)x);

这两种方法都会遍历所有元素,并将它们转换为给定类型,因此我认为最好返回 IList<List<int>>(如果可能的话)。


0
问题出在你的方法返回类型上。修改你的方法签名,将返回类型改为IList<List<int>>而不是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
    }

现在它将正常工作,因为您的方法现在返回一个IList,其中包含List<int>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接