在C#中,为什么不能将List对象存储在List变量中

时间:2020-03-05 18:38:43  来源:igfitidea点击:

似乎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 &lt;string>转换为System.Collections.Generic.List &lt;object>

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

解决方案

回答

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

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

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

回答

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

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

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

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

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

回答

迈克(Mike),我相信双方都不允许使用自律性

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

回答

这与协方差有很大关系,例如,泛型类型被视为参数,如果参数不能正确解析为更特定的类型,则操作将失败。这样的含义是,我们实际上不能转换为更通用的类型(如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的所有内容。

回答

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

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

这不是我们所要的,但应该可以解决。

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

回答

我们不能在具有不同类型参数的泛型类型之间进行转换。专门的泛型类型不构成同一继承树的一部分,不相关的类型也是如此。

为此,在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();

回答

实际上,是因为代码随后会崩溃(因为列表确实是List,因此,我们不要尝试在" ol"列表变体中放置任何奇数的" object"(如List &lt;object>似乎允许的那样)。 &lt;string>,并且仅接受String类型的对象)。这就是为什么我们不能将变量转换为更通用的规范的原因。

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

回答

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

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

Cperforms运行时检查可防止我们将" int"放入" a"。

回答

是的,我们可以从.NET 3.5:

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

回答

我认为C4.0将实际上支持此(逆方差)。
http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

回答

这是针对任何可以隐式转换内容的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的示例)

回答

我有一个:

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

我要用在List <object>中收集的数据填充它
最终对我有用的是这个:

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

将其转换为想要从该类型中获取IEnumerable的类型,然后将IEnemuerable转换为所需的List <>。