关于.net:在C#中,为什么不能列出< string>

关于.net:在C#中,为什么不能列出< string>

In C#, why can't a List<string> object be stored in a List<object> variable

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

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

导致无法隐式地将类型System.Collections.Generic.List转换为System.Collections.Generic.List

然后。。。

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

导致无法将类型System.Collections.Generic.List转换为System.Collections.Generic.List

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


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

作为旁注,我认为无论您是否可以进行反向转换,都将更加重要:

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

我有一段时间没有使用过C,所以我不知道这是否合法,但这种类型的演员表实际上(潜在)是有用的。在本例中,您将从一个更通用的类(对象)转到一个更具体的类(字符串),该类(字符串)从通用类扩展到更具体的类(字符串)。这样,如果您添加到字符串列表中,就不会违反对象列表。

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


如果您使用的是.NET 3.5,请看一下Enumerable.Cast方法。它是一个扩展方法,因此您可以直接在列表中调用它。

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

这不完全是你想要的,但应该做到这一点。

编辑:正如Zooba所指出的,然后可以调用ol.to list()以获取列表


不能在具有不同类型参数的泛型类型之间强制转换。专门化的泛型类型不构成同一继承树的一部分,因此是不相关的类型。

要执行此3.5版之前的操作:

1
2
3
4
5
6
7
8
9
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:

1
2
3
4
5
6
7
8
9
10
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();

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

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

这就是为什么存在类似于convert()的IEnumerable扩展方法,它允许您轻松地为集合中存储的项提供转换,这与从一个集合到另一个集合的转换一样简单。


这与协方差有很大关系,例如,将泛型类型视为参数,如果参数不能正确解析为更具体的类型,则操作失败。这意味着,您实际上无法强制转换为更一般的类对象类型。如rex所述,list对象不会为您转换每个对象。

您可能希望尝试使用FF代码:

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

或:

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

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


是的,您可以从.NET 3.5:

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


迈克-我相信在C中也不允许有矛盾。

有关更多信息,请参见clr中的泛型类型参数方差。


我认为C 4.0中会支持这一点(相反)。http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx


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

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

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


实际上,这样就不会试图在"ol"列表变量中放入任何奇怪的"object"(如List所允许的那样),因为您的代码会崩溃(因为列表实际上是List并且只接受字符串类型的对象)。这就是为什么不能将变量强制转换为更通用的规范的原因。

在Java上,反过来说,你没有泛型,而在运行时,所有的东西都是对象列表,而且你真的可以在你所谓的严格类型化的列表中填入任何奇怪的对象。搜索"泛化泛型",以查看对Java问题的更广泛的讨论…


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


嗯,多亏了之前的评论,我找到了两种方法来找到它。第一种方法是获取元素的字符串列表,然后将其转换为IEnumerable对象列表:

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

第二种方法是避免IEnumerable对象类型,只需将字符串强制转换为对象类型,然后在同一句话中使用函数"tolist()":

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

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


我有一个:

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

我准备用一个List中收集的数据来填充它。最终对我起作用的是这个:

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

.Cast它转换成你想从该类型中得到IEnumerable的类型,然后把IEnemuerable转换成你想要的List<>类型。


对于任何内容可以隐式强制转换的IList,这是另一个.NET 3.5之前的解决方案。

1
2
3
4
5
6
7
8
9
10
11
public IList ConvertIList<D, B>(IList<D> list) where D : B
{
    List newList = new List();

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

    return newList;
}

(基于Zooba的例子)


推荐阅读