我可以用IEnumerable <derivedclass <subclass >>调用采用IEnumerable <baseclass <superclass >>的方法吗

时间:2018-06-21 08:13:27

标签: c# .net oop .net-core

我有一些与此等效的代码(尽管这是一种简化):

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

我得到一个错误,告诉我转换来自:

IList<SpecializedCatVet<Cat>>IEnumerable<Vet<Animal>>是不可能的。怎么会这样?在此之前,它们不是通用的,而且我还有其他一些问题,因为我无法覆盖vet接口的takeCareOf方法。我曾期望,由于可以用Dog列表轻松实例化Animal的IEnumerable,所以只要解析泛型的集合,只要它们的类型参数是所需类型的派生类,解析情况便是如此。但是,事实并非如此,我似乎无法弄清楚为什么或如何正确执行此操作。

感谢阅读。

更新:我接受JLRishe的回答,这是很合理的。非常感谢。

2 个答案:

答案 0 :(得分:6)

这是不允许的,因为如果允许,您可能会这样:

var cat = new Cat();

treatSickAnimal(DogVets, cat);

从本质上说,试图迫使狗兽医对待猫。

您可以通过将方法设置为动物参数通用来对此进行补救:

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

这应该允许您的代码正确编译,并确保您不要尝试强迫任何兽医来对待猫。


旁注注释-除非计划使用DogCat的特殊类型(子类)以及专门用于这些子类的兽医,否则可以简化SpecializedDogVetSpecializedCatVet如下:

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

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

然后您将引用以下类型:

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

答案 1 :(得分:1)

您不能这样做,因为您的Vet<T>接口当前是不变的,请注意,您可以通过指定它是输入参数来使其不变,如下所示:

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

之所以可行,是因为在各处的T都被用作输入参数。

要编译代码,您可以执行@JLRishe所说的,或者可以更改逻辑并设置T out put参数,但是要执行此操作,您需要更改您的Vet实现,以便在所有使用T的地方都需要处于输出位置,只是一个例子:

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

因此,总而言之,您可以将基类传递给接口或委托,在接口或委托中它们的类型参数定义为 out put,并且当类型参数定义为 in <时,可以传递更多派生类。 / strong>放入。

这实际上是有关协方差和协方差的惊人文章,如果我被您迷住了,您肯定应该对其进行检查http://tomasp.net/blog/variance-explained.aspx/