我有一些与此等效的代码(尽管这是一种简化):
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的回答,这是很合理的。非常感谢。
答案 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);
}
}
这应该允许您的代码正确编译,并确保您不要尝试强迫任何兽医来对待猫。
旁注注释-除非计划使用Dog
和Cat
的特殊类型(子类)以及专门用于这些子类的兽医,否则可以简化SpecializedDogVet
和SpecializedCatVet
如下:
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/