简体   繁体   中英

C# how to pass a list of objects to a constructor?

I want to pass a list of objects to the constructor of a NavigationPage so that I can populate the contents of the ListView on that page.

The objects will be different types that are all inherited from the same parent which is an abstract class called "Animal" defined as follows:

abstract class Animal { ...

The inherited classes are like this:

public class Horse : Animal { ...

public class Cow : Animal { ...

public class Dog : Animal { ...

The page that displays this list is a class inherited from NavigationPage as follows:

public List<Animal> Animals = new List<Animal>();

    public AnimalListPage(string title, List<Animal> animals) {
    ....

and I create the AnimalListPage object as follows:

var horses = new List<Horse>() {
    new Horse("Silver", 65, 70),
    new Horse("Tucker", 60, 68)
};
horsesListPage = new AnimalListPage("Horses", horses);
....

EDIT: in response to some of the suggestions; To clarify: I need to preserve the object type of "Horse", "Cow", "Dog", etc. in the list because each Animal type has unique attributes to be displayed. Also, each instance of Animal List will only ever have one type, ie there will never be a list that combines Horses, Cows, and Dogs.

But I get the error:

Error CS1503 Argument 2: cannot convert from System.Collections.Generic.List<AnimalTracker.Horse> to System.Collections.Generic.List<AnimalTracker.Animal>

Do I have to "cast" the "Horse" List to type "Animal" somehow? Or make the "AnimalListPage()" constructor less type specific? How do I do that?

Note: I'm developing using Xamarin (though I don't think that is relevant to this issue).

Just make your list a List<Animal> as the constructor expects:

var horses = new List<Animal>() {
    new Horse("Silver", 65, 70),
    new Horse("Tucker", 60, 68)
};

If you don't control the source of the value and it can be a List<T> of any derived type, you'd want to perform a cast operation to use it as a List<Animal> :

var horses = someExistingList.Cast<Animal>().ToList();

Here are some options:

The easy solution is to use a List<Animal> instead of a List<Horse> . Then your code will comply to the type requested by the constructor.

More complex is making AnimalListPage generic. Then you can strongly type the Animal type you want to use in AnimalListPage . This will only work if you have one single type of Animal you want to use.

The last option is the use of an co-variant interface, IAnimal , and use that as list type. All Animal types should implement that, but I guess you can make that happen in your base class already. Or use a co-variant collection interface, like IEnumerable<Animal> .

Let me explain the nature of this situation a little.
You cannot implicitly convert a collection to a collection of base type, even if it sounds safe and correct.
Imagine the following case:

public void CalledMethod(List<Animal> animals)
{ 
    animals.Add(new Dog()); 
    // error! you've tried to insert a dog into List<Horse>
}

public void CallingMethod()
{
    var horses = new List<Horse>() {
      new Horse("Silver", 65, 70),
      new Horse("Tucker", 60, 68)
    };

    CalledMethod(horses);

    string result = horses[2].RideOrDoSomeOtherHorseOnlyThings(); // it would be strange
}

You need to pass a collection of Animal by either declaring List<Animal> instead of List<Horse> , or using .Cast<Animal>() extension method, as already fully described by other answerers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM