В этой статье я бы хотел дать свое объяснение как работает ковариация и контрвариация в C#.
Что такое ковариация и контрвариация
Предположим у нас есть два типа - тип Animal и тип Cat и Cat наследуется от Animal - то есть мы можем Cat привести к Animal и есть третий тип, который использует типы Cat и Animal, допустим это тип Zoo. Тип Zoo является ковариантным, если мы можем Zoo<Cat>
привести к Zoo<Animal>
и является контрвариантным, если мы можем Zoo<Animal>
привести к Zoo<Cat>
. Я использовал синтаксис обобщения в примере, но вообще - это будет не обязательно обобщение - смысл в том, что один тип использует другие типы и поддерживает приведения этих типов в контексте себя.
Классическим является пример с приведением списка:
IEnumerable<string> strCollection = new List<string>() {"string"};
IEnumerable<object> objectCollection = strCollection;
string - наследуется от object и коллекцию string мы можем привести к коллекции object.
То есть, если некоторый метод, в качестве принимаемого параметра использует IEnumerable<object>
, то мы можем передать в этот метод переменную с типом IEnumerable<string>
и она будет приведена к нужному типу.
Смысл ковариации и контрвариации в C#
Ковариация и контрвариация в C# существует только в контексте методов. Давайте представим, что под типом Zoo из примера выше мы подразумеваем метод, а типы Cat и Animal - это типы возвращемого значения или входного параметра - то есть под Zoo<SomeType>
(где SomeType - Cat или Animal) мы подразумеваем: