泛型列表<list <t>&gt; </list <t>

时间:2013-03-21 14:35:04

标签: c# generics

如果我List<T>获得了MyClass类型的列表,例如List<List<MyClass>>MyClass是来自MyClassB的父类。为什么我不能做以下事情?

List<List<MyClass>> allLists = new List<List<MyClass>>();

List<MyClassB> myList = new List<MyClassB>();
myList.Add(new MyClassB());

//And now the point which dont work
allLists.Add(myList);

如果我实现了一个方法,我可以说SomeClass<T> ... where T : MyClass,我的列表问题有类似的东西吗?

这样我可以将任何子类的列表添加到我的第一级列表中吗?

8 个答案:

答案 0 :(得分:9)

class Animal {}
class Tiger : Animal {}
class Giraffe : Animal {}
...
List<Giraffe> giraffes = new List<Giraffe>();
List<List<Animal>> lists = new List<List<Animal>>();
lists.Add(giraffes); // Illegal!

你的问题是“为什么这是非法的?”答案是:假设它是合法的,让我们继续......

List<Animal> animals = lists[0]; // Obviously typesafe.
animals.Add(new Tiger()); // Obviously typesafe

我们刚刚将一只老虎添加到长颈鹿名单中。

由于后两个步骤显然是类型安全的,因此不能是类型安全的地方必须是Add(giraffes)

现在,从C#4开始,这确实有效:

List<IEnumerable<Animal>> lists = new List<IEnumerable<Animal>>();
lists.Add(giraffes);

为什么合法?因为Add上没有IEnumerable<T>方法:

IEnumerable<Animal> animals = lists[0];

现在我们不能违反类型安全,因为如果我们只通过IEnumerable<T>访问它,就无法将老虎放入长颈鹿列表中。

顺便说一句,有人几乎每天都会问这个问题。在网上搜索“C#covariance and contravariance”,你会得到更多关于它的信息。

答案 1 :(得分:8)

您无法做到的原因如下:假设allLists.Add(myList)有效。然后编译器会知道allLists[0]List<MyClass>,所以以下就可以了:

allLists[0].Add(new MyClassX());

那将是运行时错误,因为allLists[0]实际上是List<MyClassB>。它无法保留MyClassX个对象。

如果您更改代码以使myListList<MyClass>,则代码可以正常运行:

List<MyClass> myList = new List<MyClass>();
myList.Add(new MyClassB()); // This works, because MyClassB extends MyClass
allLists.Add(myList); // This works, too

答案 2 :(得分:2)

你必须使用一个允许派生类(称为covariant interface)的接口作为内部集合:

var allLists = new List<IEnumerable<MyClass>>();

答案 3 :(得分:0)

List<List<MyClass>> allLists = new List<List<MyClass>>();

您要添加的列表不是同一类型。 你可以这样做:

interface IMyClass
{
    //some properties
}

您的所有子类都必须从IMyClass继承。然后你会有这样的清单。

List<List<IMyClass>> allLists = new List<List<IMyClass>>();

答案 4 :(得分:0)

问题是AllLists是强类型的,包含MyClass列表。 MyList是强类型的,用于包含MyClassB的实例。

虽然您可以在列表&gt;中存储列表,但您当前的代码中存在类型不匹配。

答案 5 :(得分:0)

这将解决问题:

List<MyClass> myList = new List<MyClass>();

答案 6 :(得分:0)

你需要告诉编译器我认为MyClass是MyClassB的父级。关于where关键字的This article可能会对您有所帮助

答案 7 :(得分:0)

尽管MyClassB是MyClass的子类型,但这并不意味着List是List的子类型。你想问的问题是List为什么不与T协变(这会导致List成为List的子类型,其中T是Y的子类型。查找术语协方差和逆变。

答案是双重的。首先,列表是在C#实现共同和逆变之前实施的。但最重要的是,如果T是“out”类型,那么你只能是协变的。 由于您可以将内容推送到列表中,因此它不是外部类型。 IEnumerable只发出类型为T的对象。因此,它可以是协变的。