【问题标题】:In C#, is it possible to cast a List<Child> to List<Parent>?在 C# 中,是否可以将 List<Child> 强制转换为 List<Parent>?
【发布时间】:2015-10-14 19:17:35
【问题描述】:

我想做这样的事情:

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

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

【问题讨论】:

标签: c# inheritance casting


【解决方案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
    猜你喜欢
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    相关资源
    最近更新 更多