【问题标题】:How to create a nested List of derived classes如何创建派生类的嵌套列表
【发布时间】:2017-06-21 21:29:27
【问题描述】:

我正在编写一个应用程序来处理 WMI 对象。我有一个带有List<Component> 属性的Computer 类,它是Component 一个抽象基类,所有派生类都来自该类,例如MonitorComputerUnitPrinter 等等。

考虑到 WMI 对象种类繁多,我第一次决定使用自定义 .NET 泛型(我是初学者),这就是代码 - 只是相关行:

public class Computer
{
    public List<Component> ListOfComponents { get; set; }
}

public class Component
{ 
    public NameSpaceBase[] WMI_ClassArray { get; set; }
}

public class Monitor : Component
{ }

public class ComputerUnit : Component
{ }

public static void Main()
{
    Computer computer = new Computer(hostName);

    Monitor monitor = computer.Get<Monitor>(new NameSpaceBase[] {
                                                new WMI_Monitor() });

    ComputerUnit computerUnit = computer.Get<ComputerUnit>(
                                    new NameSpaceBase[] {
                                        new WMI_Bios(),
                                        new WMI_ComputerSystem() });

    computer.ListOfComponents.Add(monitor);
    computer.ListOfComponents.Add(computerUnit);
}

直到我意识到我想将每个组件都视为一个列表本身之前它工作得很好,因为我需要将多个监视器和/或其他多个设备分开,所以我相应地更改了我的属性和方法:

public class Computer
{
    public List<List<Component>> ListOfComponents { get; set; }
}

public static void Main()
{
    Computer computer = new Computer(hostName);

    List<Monitor> monitor = computer.Get<Monitor>(new NameSpaceBase[] {
                                                new WMI_Monitor() });

    List<ComputerUnit> computerUnit = computer.Get<ComputerUnit>(
                                new NameSpaceBase[] {
                                    new WMI_Bios(),
                                    new WMI_ComputerSystem() });

    computer.ListOfComponents.Add(monitor);
    computer.ListOfComponents.Add(computerUnit);
}

但现在我在最后两行得到error CS1503: Argument 1: cannot convert from 'System.Collections.Generic.List&lt;Machine.Components.Monitor&gt;' to 'System.Collections.Generic.List&lt;Machine.Components.Component&gt;'

我不明白错误在哪里,因为如果我注释最后两行,我可以看到 List 和 List 对象已正确创建、类型正确并填充了预期的数据。

故事的底线:我不明白为什么我不能将List&lt;Monitor&gt; 对象添加到List&lt;List&lt;Component&gt;&gt;,而我可以Monitor 对象添加到@ 987654334@.

【问题讨论】:

  • 这与协方差有关,但我不确定为什么它不起作用,因为 List 是协变的。
  • @rory.ap 因为协方差只与接口和委托有关
  • @rory.ap,不支持类中的协方差
  • 啊,就是这样。正确的。所以把Computer.ListOfComponents的类型从List&lt;Component&gt;改成IList&lt;Component&gt;
  • 另一种解决方案是,如果您使用 Component 类并将 (Child)Components 属性添加为 List&lt;Component&gt;。另请参阅Composite pattern

标签: c# generics casting


【解决方案1】:

具体回答你的问题:

我不明白为什么我不能将 List 对象添加到 List>,而我可以将 Monitor 对象添加到 列出。

这与询问为什么不能将List&lt;Derived&gt; 分配给List&lt;Base&gt; 的原因相同,其中Derived : Base

某些语言允许允许这样做,但 C# 不允许。让我们来看看原因。

考虑这些类:

class Animal
{
}

class Cat : Animal
{
    public void Meow() {}
}

class Dog : Animal
{
    public void Bark() { }
}

现在假设你有一个狗列表:

List<Dog> dogs = new List<Dog> {new Dog()};

您不得进行以下操作:

List<Animal> animals = dogs;  // Not allowed - let's pretend it is!

好的,让我们假设上面的行编译。执行后,animals 列表将成为对dogs 的引用,即List&lt;Dog&gt;。记住这个重要的事实!

现在让我们执行以下操作:

animals.Add(new Cat());

看起来不错吧?没有。我们刚刚添加了一个Catdogs,它现在包含两个元素; CatDog

现在如果我们这样做dogs[1].Bark();会发生什么?

答案是程序会在运行时崩溃,因为猫不会叫!当然,这实际上不可能发生,因为您不允许这样做List&lt;Animal&gt; animals = dogs;


可能的解决方案?

您可以使用IReadOnlyList&lt;T&gt; interface 代替IList&lt;T&gt;

IReadOnlyList<Animal> animals = dogs; // Compiles OK.

这是允许的,因为它是这样声明的:

public interface IReadOnlyList<out T> : IReadOnlyCollection<T>, 
IEnumerable<T>, IEnumerable

out T 指定接口为covariant

因为IReadOnlyList不允许修改,所以支持协方差是可能的。

【讨论】:

  • 改为List> 运行流畅。谢谢!
【解决方案2】:

解决您对列表列表的要求的一种方法是简单地使用列表,然后在您需要特定类型的所有组件时进行过滤。

monitors = computer.ListOfComponents.OfType<Monitor>();

这样的设计也使得修改这个集合更容易,因为您不必处理第一次添加特定类型的组件的情况,在这种情况下您需要创建并添加子列表。

【讨论】:

    【解决方案3】:
    computer.ListOfComponents.Add(monitor.Cast<Component>().ToList());
    computer.ListOfComponents.Add(computerUnitCast<Component>().ToList());
    

    【讨论】:

    • 无论它是否有效,您都应该为此提供一些上下文/解释。
    • 它确实有效。我没有投反对票,但我认为您的解决方案只是提供了设计缺陷的捷径。但有效!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多