How to create a nested list of derived classes

I am writing an application to work with WMI objects. I have a class Computer

with a property List<Component>

that is a Component

base class abtract, from which come all derived classes, such as Monitor

, ComputerUnit

, Printer

, etc.

Given the wide variety of WMI objects, I decided to use custom .NET generators for the first time (I'm a newbie) and that was the code - just the relevant lines:

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);
}

      

It worked fine until I realized that I wanted to treat each component as a List itself, because I needed to split multiple monitors and / or other multiple devices, so I changed my properties and methods accordingly:

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);
}

      

but now I am assigned error CS1503: Argument 1: cannot convert from 'System.Collections.Generic.List<Machine.Components.Monitor>' to 'System.Collections.Generic.List<Machine.Components.Component>'

in the last two lines.

I can't figure out where the error is, because if I comment out the last two lines, I see that the List and List objects are correctly created, of the correct type, and filled with data.

Bottom line of history: I don't understand why I can't add an object List<Monitor>

to List<List<Component>>

, whereas I can add an object Monitor

to List<Component>

.

+3


source to share


3 answers


To specifically answer your question:

I don't understand why I cannot add a List <Monitor> object to a List <List <Component -> while I can add a Monitor object to a List <Component>.

This is the same as asking why you cannot assign List<Derived>

List<Base>

where Derived : Base

.

Some languages โ€‹โ€‹allow this, but C # does not. Let's see why.

Consider these classes:

class Animal
{
}

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

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

      

Now, suppose you have a list of dogs:

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

      

You are not allowed to do the following:

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

      

Ok, let's pretend the above line is compiling. After executing it, the list animals

will be a link to dogs

which is List<Dog>

. Remember this important fact!

Now let's do the following:



animals.Add(new Cat());

      

Seems okay? Nope. We just added Cat

to dogs

, which now contains two elements; a Cat

and a Dog

.

Now what happens if we do dogs[1].Bark();

?

The answer is that the program will explode during execution because cats cannot bark! Of course this cannot happen because you are not allowed to do it List<Animal> animals = dogs;

.


Possible Solution?

There's an IReadOnlyList<T>

interface
you can use instead IList<T>

:

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

      

This is allowed because it is declared like this:

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

      

out T

indicates that the covariant .

Since IReadOnlyList

it cannot be modified, it can support covariance.

+1


source


One way to get around your requirement of a list of lists is to simply use List and then filter every time you need all components of a specific type.

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

      



This design also makes it easier to modify this collection, since you don't have to deal with the ability to add a specific type of component the first time, in which case you will need to create and add subscriptions too.

0


source


computer.ListOfComponents.Add(monitor.Cast<Component>().ToList());
computer.ListOfComponents.Add(computerUnitCast<Component>().ToList());

      

-2


source







All Articles