简体   繁体   English

清单 <IList<int> &gt;不接受清单 <List<int> &gt;?

[英]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>? 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
    }

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 由于合同允许将IList<int>任何内容添加到结果中,因此您可能拥有

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. 您的result是一个List<int>List<int> ,除了List<int>或其派生类之外,您不应被添加任何其他内容。 However, you were able to add MyCustomIList which is not related to the List<int> . 但是,您可以添加 List<int> 不相关的 MyCustomIList

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. 此特定示例中的基本问题来自“ Add操作。 If you don't need it, the IEnumerable will do 如果您不需要它,则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
}

This has been covered already . 这已经涵盖了

That's because of 那是因为

List<T> implements IList<T> but List<T>实现IList<T>

List<List<T>> does not implement IList<IList<int>> List<List<T>>不实现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 docs.microsoft.com 通过docs.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 泛型类型参数支持协方差和协变,以在分配和使用泛型类型时提供更大的灵活性

Why then does List implement IList? 为什么列表要实现IList?

It is a bit odd, since List for any type other than object does not fulfill the full contract of IList. 有点奇怪,因为除对象以外的任何类型的List都不能满足IList的全部约定。 It's probably to make it easier on people who are updating old C# 1.0 code to use generics; 这可能使正在更新旧的C#1.0代码以使用泛型的人们更容易。 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. 在大多数情况下,当您传递IList时,被调用方可以通过按索引访问列表,而不能添加任意类型的新项。

I would suggeste return IEnumerable instead of IList, will simplify your life, since List Fully implements it. 我建议返回IEnumerable而不是IList,这将简化您的工作,因为List Fully实现了它。

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 : 我不知道为什么要精确返回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();
}

Or ConvertAll() method : ConvertAll()方法

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). 两种方法都将在所有元素上运行,并将它们强制转换/转换为给定类型,因此我认为最好返回IList<List<int>> (如果可能)。

The problem is with your method return type . 问题在于您的方法return type Modify your method signature to return to IList<List<int>> rather than returning IList<IList<int>> 修改您的方法签名以返回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
    }

Now it will work fine as your method now returns an IList of List<int> 现在,它将正常工作,因为您的方法现在返回List<int>IList

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM