【问题标题】:IList<IList<int>> doesn't accept List<List<int>>?IList<IList<int>> 不接受 List<List<int>>?
【发布时间】:2019-05-17 05:30:03
【问题描述】:

List 实现了 IList,所以我希望 IList 会接受一个 List 对象 但是为什么 IList> 不接受 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
    }

【问题讨论】:

  • 你明白为什么List&lt;object&gt;不接受List&lt;string&gt;吗?
  • 返回 (IList>) 父级;
  • @jdweng:那不行,你会得到一个运行时异常。
  • @SᴇM 这不是关于将一​​种类型转换为对象类型,而是关于 IList 和 List。

标签: c# generics


【解决方案1】:

那是因为

List&lt;T&gt; 实现了IList&lt;T&gt; 但是

List&lt;List&lt;T&gt;&gt; 没有实现IList&lt;IList&lt;int&gt;&gt;

这就是为什么您的第一种方法可以按预期工作,而第二个则不能。

只需将您在第二种方法中的列表声明更改为

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

这就是协变和逆变的情况。

泛型类型参数支持协变和逆变,但您需要以这种方式定义

docs.microsoft.com

协变和逆变是指使用比最初指定的更多派生类型(更具体)或更少派生类型(更具体)的能力。泛型类型参数支持协变和逆变,为分配和使用泛型类型提供更大的灵活性

【讨论】:

    【解决方案2】:

    假设这可行。您的客户端代码是:

    var result = List_2();
    

    由于合约允许将IList&lt;int&gt; 的任何内容添加到结果中,因此您可能有

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

    然后

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

    但那是错误的!

    您的resultList&lt;int&gt; 的列表,除了List&lt;int&gt; 或其派生词之外,您不得添加任何内容。但是,您可以将与List&lt;int&gt;不相关MyCustomIList 添加到List&lt;int&gt;

    如果您需要对该问题有一个全面的了解,请在covariance and contravariance 上阅读更多信息。

    这个特定示例中的基本问题来自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
    }
    

    这是covered already

    【讨论】:

      【解决方案3】:

      List 为什么要实现 IList?

      这有点奇怪,因为除了 object 之外的任何类型的 List 都没有 履行IList的全部合同。这可能是为了让它更容易 更新旧 C# 1.0 代码以使用泛型的人;那些人 可能已经确保只有正确的类型进入 他们的名单。大多数时候,当你传递一个 IList 时, 这样被调用者可以按索引访问列表,而不是这样 它可以添加任意类型的新项目。

      我建议返回 IEnumerable 而不是 IList,这会简化你的生活,因为 List 完全实现了它。

      【讨论】:

        【解决方案4】:

        问题在于您的方法return type。修改你的方法签名以返回IList&lt;List&lt;int&gt;&gt;而不是返回IList&lt;IList&lt;int&gt;&gt;

        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
            }
        

        现在它可以正常工作了,因为您的方法现在返回 IListList&lt;int&gt;

        【讨论】:

          【解决方案5】:

          我不知道您为什么要准确返回 IList&lt;IList&lt;int&gt;&gt;,但一种方法是使用 Cast&lt;T&gt;() 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();
          }
          

          ConvertAll() method:

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

          这两种方法都将运行在所有元素上,并将它们转换/转换为给定的类型,所以我认为最好返回 IList&lt;List&lt;int&gt;&gt;(如果可能的话)。

          【讨论】:

            猜你喜欢
            • 2020-05-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多