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


85

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

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

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

然后...

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

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

当然,您可以通过将所有内容从字符串列表中拉出并一次放回一个列表中来完成此操作,但这是一个相当复杂的解决方案。


5
这将随C#4.0一起改变,因此您可能想查找协方差和逆方差。它将以类型安全的方式允许此类操作。
约翰·莱德格伦


7
John> C#4不允许这样做。考虑一下ol.Add(new object());
纪尧姆

乔恩·斯凯特(Jon Skeet)在这里回答:stackoverflow.com/a/2033921/1070906
JCH2k 2015年

Answers:


39

如果您进行这样的转换,然后将Foo类型的对象添加到列表中,则字符串的列表将不再是一致的。如果要迭代第一个引用,则将获得类强制转换异常,因为一旦您击中了Foo实例,Foo就无法转换为字符串!

作为附带说明,我认为是否可以进行反向强制转换将更为重要:

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

我已经有一段时间没有使用C#了,所以我不知道这是否合法,但是这种类型的转换实际上是(潜在地)有用的。在这种情况下,您将从一个更通用的类(对象)转到一个从通用类扩展的更特定的类(字符串)。这样,如果您添加到字符串列表,则不会违反对象列表。

有人知道或可以测试这种强制转换在C#中是否合法吗?


4
我认为您的示例也遇到了同样的问题。如果ol中包含的不是字符串,该怎么办?问题是List上的某些方法可以正常工作,例如添加/插入。但是迭代可能是一个真正的问题。
克里斯·阿默曼

3
埃里克·利珀特(Eric Lippert)在这个主题上有很多博客文章:为什么在通用方法中添加协方差和自变量约束可能是可行的,但是在类级别却可能永远无法实现。is.gd/3kQc
克里斯·

@ChrisAmmerman:如果ol其中包含不是字符串的内容,我想我希望转换在运行时会失败。但是,如果转换成功,您真正会遇到麻烦的地方是,然后添加ol的内容不是字符串。因为sl引用了同一对象,所以您现在List<string>将包含一个非字符串。该Add是问题,我想证明为什么这个代码不能编译,但如果你改变它会编译List<object> olIEnumerable<object> ol,这不具有Add。(我在C#4中进行了检查。)
Tim Goodman 2013年

进行此更改后,它会编译但抛出一个,InvalidCastException因为的运行时类型ol为still List<object>
2013年

36

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

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

这并不是您所要的,但应该可以解决。

编辑:如Zooba所述,然后可以调用ol.ToList()以获得列表


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
}

使用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();

11

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

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

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


6

这与协方差有很大关系,例如,将通用类型视为参数,如果参数不能正确解析为更特定的类型,则操作将失败。这样的含义是,您实际上不能转换为更通用的类型,例如object。正如Rex所说,List对象不会为您转换每个对象。

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

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

要么:

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

从理论上讲,ol会毫无问题地复制sl的所有内容。





1

这实际上是为了避免在“ ol”列表变体中放置任何奇怪的“对象”(List<object>似乎允许),因为那样的话,您的代码就会崩溃(因为该列表实际上是List<string>并且仅接受String类型的对象) )。这就是为什么您不能将变量转换为更通用的规范的原因。

在Java上则相反,您没有泛型,而是所有内容都是运行时的对象列表,并且您真的可以将任何奇怪的对象塞入您应该严格键入的List中。搜索“通用化泛型”以查看有关Java问题的更广泛的讨论...


1

不支持泛型的此类协方差,但实际上您可以使用数组来做到这一点:

object[] a = new string[] {"spam", "eggs"};

C#执行运行时检查,以防止将inint放入a


0

这是针对任何可以隐式转换内容的IList的.NET 3.5之前的解决方案。

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(基于Zooba的示例)


0

我有一个:

private List<Leerling> Leerlingen = new List<Leerling>();

我将用List<object> “最终对我有用的”数据收集的数据来填充它:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Cast将其转换为您IEnumerable要从该类型获取的类型,然后将其强制转换IEnemuerable为所需的类型List<>


0

嗯,由于之前的评论,我找到了两种查找方法。第一个是获取元素的字符串列表,然后将其强制转换为IEnumerable对象列表:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

第二个是避免IEnumerable对象类型,只是将字符串转换为对象类型,然后在同一句子中使用函数“ toList()”:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

我更喜欢第二种方式。我希望这有帮助。


0
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.