Разница между ковариантностью и контравариантностью в программировании заключается в том, как происходит перенос наследования типов на производные от них типы — контейнеры, обобщённые типы, делегаты и т. п.: ru.wikipedia.org
- Ковариантность сохраняет иерархию наследования исходных типов в производных типах в том же порядке. ru.wikipedia.org Например, если класс Cat наследуется от класса Animal, то перечисление IEnumerable<Cat> будет потомком перечисления IEnumerable<Animal>. ru.wikipedia.org
- Контравариантность обращает иерархию исходных типов на противоположную в производных типах. ru.wikipedia.org Например, если класс String наследуется от класса Object, а делегат Action<T> определён как метод, принимающий объект типа T, то Action<Object> наследуется от делегата Action<String>, а не наоборот. ru.wikipedia.org
Таким образом, ковариантность позволяет использовать более конкретный тип, чем заданный изначально, а контравариантность — более универсальный тип. metanit.com
Ковариантность и контравариантность являются независимыми механизмами типобезопасности, не исключающими друг друга. ru.wikipedia.org