2015-03-26 2 views
0

У меня есть структура, подобной этойC# полиморфизм и дженерик ошибка согласования типа

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> {} 

Почему я не могу Asign это?

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

Почему я не могу добавлять элементы в Dictionary, как это?

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

Как это сделать?

+1

Btw, вы написание AnimalType как «AnymalType» в своем объявлении словаря – WhiteRuski

ответ

2

Для этого вам нужно будет коварианты (или это контравариантные) параметры. К сожалению, их можно указывать только на интерфейсах или делегатах, а не на классах. Один из способов получить то, что вы хотите, будет создавать интерфейс на вершине класса 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(); 
    } 

Чтобы быть честным, хотя, код вы вывесили заставляет меня задаться вопросом, если проблема не с дизайном вашего кода, а не синтаксиса.

Обратите внимание, что хотя это скомпилировано, предупреждение от ответа Стенли действительно. Например, вы не можете добавить метод void Treat(T patient) к интерфейсу, например.

Момент, когда вы оказываетесь проверки типов во время выполнения, или получать ошибки компиляции, потому что ваш базовый класс определяет функцию, принимая T в качестве параметра, и он не будет принимать реализацию с Dog в производном классе, вы должны перепроектировать вашей программы. На самом деле, я бы серьезно подумал об этом прямо сейчас.

Код запах здесь, что у вас есть дерево наследования на Animal, которое вы имитируете другим деревом наследования Vet. Невозможность добавить правильный производный Vet для нового производного Animal даст вам головные боли.

+0

Спасибо. Работал без проблем. 'Ковариация позволяет вам использовать более производный тип, чем тот, который указан общим параметром' https://msdn.microsoft.com/en-us/library/dd469487.aspx – dinigo

+2

Обратите внимание, что ковариантные интерфейсы могут использовать только _output_ общий тип (следовательно, ключевое слово 'out'). Если вам нужен метод, требующий _input_ общего типа, вы не можете использовать ковариацию. –

+0

Что было бы правильным подходом, если бы я захотел реализовать функцию 'Treat (T animal)' внутри 'IVet'? – dinigo

6

Это классический ковариационный вопрос. A 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!!! 
0

Основная причина в том, что дженерики не поддерживают ковариацию. Следующая информация взята из книги Джона Скита C# In Depth.

Короткий ответ заключается в том, что это было бы законным, но недействительным, если бы оно было разрешено.

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

Поскольку в Словаре говорится, что он использует Животные, но фактические объекты - это кошки, которые он потерпит неудачу во время выполнения. Опять же, согласно книге, дизайнеры предпочли бы скомпилировать временные сбои вместо сбоев во время выполнения, поэтому при попытке сделать это вы получите ошибку.

0

Это называется ковариацией, и эта функция поддерживается только в делегатах и ​​интерфейсах.

Например:

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

Узнайте больше о ковариации и контрвариации in MSDN.

Смежные вопросы