繁体   English   中英

C#多态和泛型类型匹配错误

[英]C# polymorphism and generics type matching error

我有一个类似的结构

enum AnimalType {dog, cat}

class Animal{}
class Dog : Animal {}
class Cat : Animal {}

class Vet<T> where T : Animal {}
class DogVet : Vet<Dog> {}
class CatVet : Vet<Cat> {}

为什么我不能分配这个?

...
Vet<Animal> myVet = new DogVet();
...

为什么我不能像这样向Dictionary添加元素?

...
Dictionary<AnimalType, Vet<Animal>> _vetList = new Dictionary<AnimalType, Vet<Animal>>();
_vetList.Add(AnimalType.dog, new DogVet());
...

这应该怎么做?

这是一个经典的协方差问题。 Vet<Dog>不是Vet<Animal> 如果继续类比, Vet<Dog>只能治疗狗。 您不能将其用作可以治疗任何动物的兽医。

假设您有一个Treat函数:

public void Treat<T>(T patient) {}

现在,如果Vet<Dog>Vet<Animal>那么这将是可能的:

Vet<Animal> dogVet = new Vet<Dog>();
dogVet.Treat(new Cat());  // but I can only treat dogs!!!

为此,您需要协变(或逆变)类型参数。 不幸的是,您只能在接口或委托上指定这些,而不能在类上指定。 获得所需内容的一种方法是在 Vet 类之上创建一个接口,如下所示:

    // Not sure what you need this enum for, but OK...
    enum AnimalType {dog, cat}

    class Animal{}
    class Dog : Animal {}
    class Cat : Animal {}

    // Create an interface with covariant parameter
    // i.e. an IVet<T> is also an IVet<Animal> for all T deriving from Animal.
    interface IVet<out T> where T : Animal {}

    // Made Vet abstract. You can still use this to provide base implementations for concrete Vets.
    abstract class Vet<T> : IVet<T> where T : Animal {}

    class DogVet : Vet<Dog> {}
    class CatVet : Vet<Cat> {}

    static void Main()
    {
            // Must use the interface here.
            IVet<Animal> vet = new DogVet();
    }

老实说,您发布的代码让我怀疑问题是否不在于您的代码设计而不是语法。

请注意,尽管可以编译,但 D Stanley 的回答中的警告是有效的。 例如,您现在无法向接口添加方法void Treat(T patient)

当您发现自己正在检查运行时类型,或者因为您的基类定义了一个以T作为参数的函数并且它不会接受派生类中带有Dog的实现而导致编译错误时,您应该重新设计您的程序。 事实上,我现在会认真考虑这一点。

这里的代码味道是你在Animal上有一个继承树,你正在用另一个继承树Vet模仿它。 未能为新的Animal衍生物添加适当的Vet衍生物会让您头疼。

主要原因是泛型不支持协方差。 以下信息摘自 Jon Skeet 的书 c# In Depth。

简短的回答是因为如果允许,这将是合法但无效的。

Dictionary<AnimalType, Vet<Animal>> _vetList = new Dictionary<AnimalType, Vet<Cat>>();
_vetList.Add(AnimalType.Dog, new Vet<Dog>());

由于字典被告知它使用的是动物,但实际对象是猫,因此它会在运行时失败。 再次根据这本书,设计人员宁愿编译时失败而不是运行时崩溃,因此在尝试执行此操作时会出现错误。

这称为协方差,此功能仅在委托和接口中受支持。

例如:

// Covariance
IEnumerable<object> x = new List<string>();

在 MSDN 中了解有关协方差和逆变的更多信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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