在C#中,为什么List <string>对象不能存储在List <object>变量中

Mat*_*ard 83 .net c# generics covariance type-safety

似乎List对象不能存储在C#中的List变量中,甚至不能以这种方式显式转换.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;
Run Code Online (Sandbox Code Playgroud)

结果无法隐式转换System.Collections.Generic.List<string>System.Collections.Generic.List<object>

然后...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
Run Code Online (Sandbox Code Playgroud)

结果无法将类型转换System.Collections.Generic.List<string>System.Collections.Generic.List<object>

当然,您可以通过从字符串列表中提取所有内容并将其一次放回一个来实现,但这是一个相当复杂的解决方案.

Mik*_*one 38

可以这样想,如果你要做这样的演员,然后在列表中添加一个Foo类型的对象,字符串列表就不再一致了.如果你要迭代第一个引用,你会得到一个类转换异常,因为一旦你点击Foo实例,Foo就无法转换为字符串!

作为旁注,我认为无论你是否可以进行反向投射都会更为重要:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;
Run Code Online (Sandbox Code Playgroud)

我有一段时间没有使用C#,所以我不知道这是否合法,但这种演员实际上(可能)是有用的.在这种情况下,您将从更通用的类(对象)转到更一般的类(字符串),该类从一般类扩展.这样,如果添加到字符串列表中,则不会违反对象列表.

有人知道或可以测试这样的演员是否合法C#?

  • 我认为你的例子遇到了同样的问题.如果ol中有一些不是字符串的东西,会发生什么?问题是List上的一些方法可以正常工作,例如添加/插入.但迭代可能是一个真正的问题. (4认同)
  • Eric Lippert有一系列关于这个主题的博客文章:为什么它可能会为通用方法添加协方差和逆变约束,但可能永远不会像我们在课堂上那样工作.http://is.gd/3kQc (3认同)

Ray*_*Ray 36

如果您使用的是.NET 3.5,请查看Enumerable.Cast方法.这是一种扩展方法,因此您可以直接在List上调用它.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();
Run Code Online (Sandbox Code Playgroud)

这不是你要求的,但应该做的伎俩.

编辑:如Zooba所述,您可以调用ol.ToList()来获取List


Zoo*_*oba 14

您不能在具有不同类型参数的泛型类型之间进行转换.专用泛型类型不构成同一继承树的一部分,因此是不相关的类型.

要在NET 3.5之前执行此操作:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}
Run Code Online (Sandbox Code Playgroud)

使用Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
Run Code Online (Sandbox Code Playgroud)


Rex*_*x M 11

原因是List<>,对于大多数目的,类似的通用类在外部被视为普通类.例如,当你说List<string>()编译器说ListString()(包含字符串)时.[技术人员:这是一个非常简单的英语版本的正在发生的事情]

因此,显然编译器不够聪明,无法通过强制转换其内部集合的项目将ListString转换为ListObject.

这就是为什么IEnumerable的扩展方法如Convert()允许您轻松地为存储在集合中的项目提供转换,这可能就像从一个集合转换到另一个集合一样简单.


Jon*_*jap 6

这与协方差有很大关系,例如,泛型类型被视为参数,如果参数没有正确解析为更具体的类型,则操作失败.这样的含义是你真的不能像对象那样转换为更通用的类型.并且如Rex所述,List对象不会为您转换每个对象.

您可能想要尝试使用ff代码:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);
Run Code Online (Sandbox Code Playgroud)

要么:

List<object> ol = new List<object>();
ol.AddRange(sl);
Run Code Online (Sandbox Code Playgroud)

ol(理论上)将没有问题地复制sl的所有内容.


Tam*_*ege 5

是的,你可以从.NET 3.5开始:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Run Code Online (Sandbox Code Playgroud)