简体   繁体   中英

Can i call a method that takes IEnumerable<Baseclass<superclass>> with IEnumerable<Derivedclass<subclass>>

I have some code that's equivalent to this (though this is a simplification):

namespace AnimalHospital
{
    public class Animal { }
    public class Dog : Animal { }
    public class Cat : Animal { }

    public interface Vet<T> where T : Animal 
    { 
        void takeCareOf(T animal); 
    }
    public abstract class SpecializedDogVet<T> : Vet<T> where T : Dog
    {
       public abstract void takeCareOf(T dog); 
    }

    public abstract class SpecializedCatVet<T> : Vet<T> where T : Cat
    {
        public abstract void takeCareOf(T cat);
    }

    public class AnimalHospital
    {
        public IList<SpecializedCatVet<Cat>> CatVets = new List<SpecializedCatVet<Cat>>();
        public IList<SpecializedDogVet<Dog>> DogVets = new List<SpecializedDogVet<Dog>>();

        private void treatSickAnimal(IEnumerable<Vet<Animal>> vets, Animal patient)
        {
            foreach(var vet in vets)
            {
                vet.takeCareOf(patient);
            }
        }

        public void treatSickCat(Cat cat)
        {
            treatSickAnimal(CatVets, cat);
        }

        public void treatSickDog(Dog dog)
        {
            treatSickAnimal(DogVets, dog);
        }
    }
}

I get an error, telling me that conversion from:

IList<SpecializedCatVet<Cat>> to IEnumerable<Vet<Animal>> is not possible. How can this be? Before this they were nonegeneric, and i had some other problems, as i could not override the vet interfaces takeCareOf method. I had expected that as IEnumerable of Animal can easily be instantiated with a list of Dog, the same would be the case with parsing a collection of generics as long as their type parameter is a derirative of the required type. This is not the case though, and I seem unable to figure out why, or how to do this properly.

Thanks for reading.

UPDATE: I'm Accepting JLRishe's answer, makes perfect sense. Thank you very much.

This is not allowed, because if it were allowed, you could something like this:

var cat = new Cat();

treatSickAnimal(DogVets, cat);

And essentially try to force dog vets to treat a cat.

You can remedy this by making your methods generic on the animal parameter:

private void treatSickAnimal<T>(IEnumerable<Vet<T>> vets, T patient) where T : Animal
{
    foreach (var vet in vets)
    {
        vet.takeCareOf(patient);
    }
}

This should allow your code to compile correctly and ensure that you don't try to force any dog vets to treat cats.


Side note note - unless you plan on having specialized types (subclasses) of Dog and Cat and vets that are specialized for those subclasses, you can simplify the definition of SpecializedDogVet and SpecializedCatVet to be like this:

public abstract class SpecializedDogVet : Vet<Dog>
{
    public abstract void takeCareOf(Dog dog);
}

public abstract class SpecializedCatVet : Vet<Cat>
{
    public abstract void takeCareOf(Cat cat);
}

You would then refer to the types like this:

public IList<SpecializedCatVet> CatVets = new List<SpecializedCatVet>();
public IList<SpecializedDogVet> DogVets = new List<SpecializedDogVet>();

You cant because your Vet<T> interface is currently invariant, notice you can make it contravariant by specifying that it is input parameter like so:

public interface Vet<in T> where T : Animal
{
    void takeCareOf(T animal);
}

This is possible because everywhere T is used as in put parameter.

In order your code to compile you can do what @JLRishe said, or you can change your logic and make T out put parameter, but in order to do that you need to change your Vet implementation so everywhere T is used it needs to be in position of output just an example:

public interface Vet<out T> where T : Animal
{
    T takeCareOf();
}

So in conclusion, you can pass base class to interfaces or delegates where they have type parameters defined as out put and you can pass more derived classes when type parameters are defined as in put.

This is actually AMAZING article for covariance and contravariance if I got you hooked you should definitely check it http://tomasp.net/blog/variance-explained.aspx/

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