在 C# 中,是不是可以将 List<Child> 强制转换为 List<Parent>?

Posted

技术标签:

【中文标题】在 C# 中,是不是可以将 List<Child> 强制转换为 List<Parent>?【英文标题】:In C#, is it possible to cast a List<Child> to List<Parent>?在 C# 中,是否可以将 List<Child> 强制转换为 List<Parent>? 【发布时间】:2010-12-19 03:59:48 【问题描述】:

我想做这样的事情:

List<Child> childList = new List<Child>();
...
List<Parent> parentList = childList;

但是,因为 parentList 是 Child 的祖先的 List,而不是直接的祖先,所以我无法做到这一点。是否有解决方法(除了单独添加每个元素)?

【问题讨论】:

这不是完全重复的,但这里有一个非常相似的问题:***.com/questions/1569774/ienumerablet-conversion 【参考方案1】:

使用 LINQ:

List<Parent> parentList = childList.Cast<Parent>().ToList();

Documentation for Cast&lt;&gt;()

【讨论】:

请注意,这会创建childList 的副本,与@Andre Pena 的不使用LINQ 版本完全相同。 List parentList = childList.OfType().ToList();也有效,并且在您不太确定起始列表的内容的情况下更可取。 如果 Child 继承自 Parent,那么您可以 100% 确定演员仍然可以工作。 lextm:不适用于列表。 List&lt;&gt;s 既不是协变的也不是逆变的。 @recursive true,因此您可以使用新的 Parent 对象列表进行迭代等。但是 List 是一个新对象,而不是对原始对象的引用,因此如果删除其中一个元素,例如,原始 List 将不会被修改。【参考方案2】:

不允许直接强制转换,因为无法使其类型安全。如果你有一个长颈鹿列表,然后你把它放到一个动物列表中,你就可以把一只老虎放到一个长颈鹿列表中!编译器不会阻止您,因为当然老虎可能会进入动物列表。编译器可以阻止你的唯一地方是不安全的转换。

在 C# 4 中,我们将支持使用引用类型参数化的 SAFE 接口和委托类型的协变和逆变。详情请看这里:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

【讨论】:

更糟糕的是,如果编译器允许您这样做,您的列表最终会比预期的要小,因为老虎会吃掉一些长颈鹿。 如果“父”的类型参数是接口,有什么变化吗?看来协变集合接口解决了 OP 的原始问题。【参考方案3】:

您可以通过使用应用扩展方法的 Linq 方法来做到这一点,即:

List<Parent> parentList = childList.Cast<Parent>().ToList();

【讨论】:

【参考方案4】:

是的,你可以这样做

var result = List.And(x => x.Parent.All(b => b.ParentId == value));

【讨论】:

这似乎与 ORM 对象层次结构有关。有用的示例需要更多上下文。【参考方案5】:

早在 2009 年,Eric 就曾戏弄我们说 C# 4 会发生变化。那么我们今天的立场是什么?

我的答案中使用的类可以在底部找到。为了更容易理解,我们将使用 Mammal 类作为“父”类,将 CatDog 类用作“子类”。猫和狗都是哺乳动物,但猫不是狗,狗不是猫。

这仍然不合法,也不可能:

List<Cat> cats = new List<Cat>();

List<Mammal> mammals = cats;

为什么不呢?猫是哺乳动物,为什么我们不能将猫的列表分配给List&lt;Mammal&gt;

因为,如果允许我们在 List&lt;Mammal&gt; 变量中存储对 List&lt;Cat&gt; 的引用,我们就可以编译以下代码以将狗添加到猫列表中:

mammals.Add(new Dog());

我们不能允许这样!请记住,mammals 只是对cats 的引用。 Dog 不是 Cat 的后代,并且在 Cat 对象列表中没有任何业务。

Starting with .NET Framework 4,几个泛型接口具有使用 C# 4 中引入的out 泛型修饰符关键字声明的协变类型参数。这些接口中有IEnumerable&lt;T&gt;,它当然是由List&lt;T&gt; 实现的。

这意味着我们现在可以List&lt;Cat&gt; 转换为IEnumerable&lt;Mammal&gt;

IEnumerable<Mammal> mammalsEnumerable = cats;

我们无法向mammalsEnumerable 添加新的Dog,因为IEnumerable&lt;out T&gt; 是“只读”接口,即它没有Add() 方法,但我们现在可以使用cats 可以使用 IEnumerable&lt;Mammal&gt; 的任何地方。例如,我们可以将mammalsEnumerableList&lt;Dog&gt; 连接以返回一个新序列:

void Main()

    List<Cat> cats = new List<Cat>  new Cat() ;
    IEnumerable<Mammal> mammalsEnumerable =
        AddDogs(cats); // AddDogs() takes an IEnumerable<Mammal>
    Console.WriteLine(mammalsEnumerable.Count()); // Output: 3. One cat, two dogs.


public IEnumerable<Mammal> AddDogs(IEnumerable<Mammal> parentSequence)

    List<Dog> dogs = new List<Dog>  new Dog(), new Dog() ;
    return parentSequence.Concat(dogs);


类定义:

public abstract class Mammal  

public class Cat: Mammal  

public class Dog : Mammal  

【讨论】:

++为唯一找到问题症结的:只读接口 要点?这适用于 IEnumerable 但不适用于 List

以上是关于在 C# 中,是不是可以将 List<Child> 强制转换为 List<Parent>?的主要内容,如果未能解决你的问题,请参考以下文章

C# 将Object类转化为List<T>类,其中T的类型不是object类型。

C#中如何判断一条数据是不是在某个list<T>集合中?

C#中list<>定义的变量,我用foreach()循环查找 与 list<> .find 查找两个哪个效率高?后者是不是用了算法

在 Typescript 中,有没有办法将一个类写成一个数组,所以我可以做 class[i],就像 C# 中的 List<T>

如何将List转换为一个对象

C#一个List或Array集合里是不是有相同数据