簡體   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