1

例如,如果我有List<T>which 从 type 获得一些列表MyClassList<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,我的列表问题是否有类似的东西?

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

4

8 回答 8

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方法 on IEnumerable<T>

IEnumerable<Animal> animals = lists[0];

现在我们不能违反类型安全,因为如果我们只通过IEnumerable<T>.

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

于 2013-03-21T14:41:22.473 回答
8

你不能这样做的原因如下:想象那allLists.Add(myList)行得通。然后编译器会知道那allLists[0]是 a List<MyClass>,所以下面的就可以了:

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

那将是一个运行时错误,因为allLists[0]实际上是一个List<MyClassB>. 它不能容纳MyClassX物体。

如果您将代码更改myList为 a List<MyClass>,则您的代码将起作用:

List<MyClass> myList = new List<MyClass>();
myList.Add(new MyClassB()); // This works, because MyClassB extends MyClass
allLists.Add(myList); // This works, too
于 2013-03-21T14:39:01.953 回答
2

您必须使用允许派生类的接口(称为协变接口)作为内部集合:

var allLists = new List<IEnumerable<MyClass>>();
于 2013-03-21T14:39:51.990 回答
0
List<List<MyClass>> allLists = new List<List<MyClass>>();

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

interface IMyClass
{
    //some properties
}

您的所有子类都必须继承自 IMyClass。然后你会有这样的列表。

List<List<IMyClass>> allLists = new List<List<IMyClass>>();
于 2013-03-21T14:37:47.800 回答
0

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

尽管您可以将 List 存储在 List> 中,但当前代码中存在类型不匹配。

于 2013-03-21T14:40:34.107 回答
0

这将解决问题:

List<MyClass> myList = new List<MyClass>();
于 2013-03-21T14:40:39.850 回答
0

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

于 2013-03-21T14:41:05.270 回答
0

虽然 MyClassB 是 MyClass 的子类型,但这并不意味着 List 是 List 的子类型。您要问的问题是为什么 List 不与 T 协变(这将导致 List 成为 List 的子类型,其中 T 是 Y 的子类型。查找术语协变和逆变。

答案是双重的。首先 List 在 C# 实现协变和逆变之前实现。但最重要的是,只有当 T 是“out”类型时,你才能是协变的。由于您可以将内容塞入列表中,因此它不是输出类型。IEnumerable 只发出 T 类型的对象。因此,它可以是协变的。

于 2013-03-21T14:45:54.633 回答