Предположим, у вас был класс C<T>
, который был ковариантным в T. Как может выглядеть его реализация? T должен быть только вне. Это означает, что C<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);
И, эй, мы просто поместили целое число в стек строк, но все получилось просто отлично! Нет причин, по которым это не может быть безопасным. Операция, которая нарушает безопасность типов в изменяемой структуре данных, может быть безопасно ковариантной для неизменной структуры данных.