Ответ 1
Предположим, что у вас был класс C<T>
, который был ковариантным в T. Какова может быть его реализация? Т должен быть только. Это означает, что C<T>
не может иметь никакого метода, который принимает T, любое свойство типа T с установщиком или любое поле типа T, потому что поля логически совпадают с атрибутами свойств; T входит.
Практически единственная полезная вещь, которую вы могли бы построить с помощью ковариантного класса, - это нечто непреложное, что касается T. Теперь, я думаю, было бы здорово иметь ковариантные непреложные списки и стеки и еще что-то вроде класса. Но эта особенность не настолько очевидна, что она явно оправдывала бы огромные затраты на создание системы типов, которая первоначально поддерживала бы ковариантные неизменные типы классов.
В комментарии выше был приведен пример того, где это было бы полезно. Рассмотрим следующий эскиз:
sealed class Stack<out T>
{
private readonly T head;
private readonly Stack<T> tail;
public T Peek() { return head; }
public Stack<T> Pop() { return tail; }
public Stack(T head, Stack<T> tail)
{
this.tail = tail;
this.head = head;
}
}
static class StackExtensions
{
public static Stack<T> Push<T>(this Stack<T> tail, T head)
{
return new Stack<T>(head, tail);
}
public static bool IsEmpty<T>(this Stack<T> stack)
{
return stack == null;
}
}
Предположим, что у вас были ковариантные классы. Теперь вы можете сказать
Stack<string> strings = null;
strings = strings.Push("hello");
strings = strings.Push("goodbye");
Stack<object> objects = strings;
objects = objects.Push(123);
И эй, мы просто нажали целое на стопку строк, но все получилось просто отлично! Там нет причин, почему это не может быть типичным. Операция, которая нарушает безопасность типов в изменяемой структуре данных, может быть безопасно ковариантной по неизменной структуре данных.