A Tiger
- это Animal
, поэтому он может делать все, что может Animal
. Если у меня есть метод, который запрашивает Animal
, я также могу передать Tiger
.
Ковариация - Передача аргумента более определенного типа
Это направление, с которым вы наиболее знакомы. Я могу передать IEnumerable<Tiger>
везде, где ожидается IEnumerable<Animal>
.
static void ListAnimals(IEnumerable<Animal> animals)
{
}
List<Tiger> tigers = new List<Tiger>();
ListAnimals(tigers);
Contravariance - Передача аргумента более общего типа.
"Против" подразумевает, что это идет "против" нормального потока преобразования. Это хитрее, потому что кажется нелогичным, пока вы не увидите его в действии.
Скажем, у меня есть функция, которая ожидает IComparer<Tiger>
и двух тигров для сравнения. Contravariance говорит, что я также могу передать более общий IComparer<Animal>
, потому что он может и сравнить двух тигров (так как Тигр - животное). Он сравнивает их в более общем виде, но это все еще безопасный тип.
static void CompareTigers(IComparer<Tiger> comparer, Tiger tiger1, Tiger tiger2)
{
comparer.Compare(tiger1, tiger2);
}
// normal - a tiger comparer can compare two tigers
IComparer<Tiger> tigerComparer = null;
CompareTigers(tigerComparer, new Tiger(), new Tiger());
// contravariance - an animal comparer can ALSO compare two tigers
IComparer<Animal> animalComparer = null;
CompareTigers(animalComparer, new Tiger(), new Tiger());
Обратите внимание, что это также работает с делегатами. Я могу передать Action<Animal>
в функцию, которая ожидает Action<Tiger>
, потому что Tiger
объекты также могут безопасно передаваться делегату Action<Animal>
.