Ковариация круче, чем полиморфизм, так же, как крольчихи круче, чем катание на коньках: это не одно и то же.
Ковариация и контравариантность (а также инвариантность и ... всеядность ... кто-нибудь?) Имеют дело с "направлением", которым могут следовать дженерики в отношении наследования. В вашем примере вы делаете то же самое, но это не значимый пример.
Рассмотрим, например, тот факт, что IEnumerable<T>
равно out T
. Это позволяет нам делать что-то вроде этого:
public void PrintToString(IEnumerable<object> things)
{
foreach(var obj in things)
{
Console.WriteLine(obj.ToString());
}
}
public static void Main()
{
List<string> strings = new List<string>() { "one", "two", "three" };
List<MyClass> myClasses = new List<MyClass>();
// add elements to myClasses
PrintToString(strings);
PrintToString(myClasses);
}
В предыдущих версиях C # это было бы невозможно, поскольку List<string>
реализует IEnumerable
и IEnumerable<string>
, , а не IEnumerable<object>
. Однако, поскольку IEnumerable<T>
равен out T
, мы знаем, что теперь он совместим для присваивания или передачи параметров для любого IEnumerable<Y>
, где T is Y
или T:Y
.
Подобные вещи можно было бы обойти в предыдущих версиях при некоторых обстоятельствах, сделав саму функцию универсальной и используя обобщенный вывод типов, приводящий к идентичному синтаксису во многих случаях. Это, однако, не решило большую проблему и ни в коем случае не было 100% обходным путем.