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

时间:2008-08-09 02:32:02

标签: c# .net generics covariance type-safety

似乎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>

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

14 个答案:

答案 0 :(得分:37)

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

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

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

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

是否有人知道或可以测试此类演员在C#中是否合法?

答案 1 :(得分:36)

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

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

这不完全是你要求的,但应该做的。

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

答案 2 :(得分: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();

答案 3 :(得分:11)

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

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

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

答案 4 :(得分:6)

这与协方差有很大关系,例如,泛型类型被视为参数,如果参数没有正确解析为更具体的类型,则操作失败。这样的含义是你真的不能像对象那样转换为更通用的类型。正如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的所有内容而不会出现问题。

答案 5 :(得分:5)

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

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

答案 6 :(得分:3)

迈克 - 我认为C#中不允许出现逆转

有关详情,请参阅Generic type parameter variance in the CLR

答案 7 :(得分:2)

答案 8 :(得分:1)

实际上,你不要试图在你的“ol”列表变体中放置任何奇怪的“对象”(因为List<object>似乎允许) - 因为你的代码会崩溃(因为列表真的是List<string>并且只接受String类型对象)。这就是为什么你不能将你的变量转换为更通用的规范。

在Java上它是相反的,你没有泛型,而是在运行时所有东西都是对象列表,你真的可以在你所谓的严格类型列表中填充任何奇怪的对象。搜索“Reified generics”以查看有关java问题的更广泛讨论......

答案 9 :(得分:1)

不支持对泛型的这种协方差,但实际上您可以使用数组执行此操作:

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

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

答案 10 :(得分: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的例子)

答案 11 :(得分:0)

我有一个:

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

我打算用List<object>收集的数据填写它 最终对我有用的是这个:

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

.Cast它是您希望从该类型获得IEnumerable的类型,然后将IEnemuerable类型转换为您想要的List<>

答案 12 :(得分: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();

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

答案 13 :(得分:0)

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