Одна из причин:
class Foo<out T>
{
T _store;
public T Get()
{
_store = default(T);
return _store;
}
}
Этот класс содержит функцию, которая не является ковариантной, потому что у нее есть поле, и для полей можно установить значения. Хотя он используется ковариантным способом, потому что ему всегда присваивается значение по умолчанию, и оно всегда будет null
только для любого случая, когда фактически используется ковариация.
Таким образом, не ясно, можем ли мы это допустить. Запрещение этого вызовет раздражение пользователей (в конце концов, это соответствует тем же потенциальным правилам, которые вы предлагаете), но разрешить это сложно (анализ уже стал немного сложным, и мы даже не начинаем охотиться за действительно сложными случаями).
С другой стороны, анализ этого намного проще:
void Main()
{
IFoo<object> foo = new Foo<string>();
Console.WriteLine(foo.Get());
}
interface IFoo<out T>
{
T Get();
}
class Foo<T> : IFoo<T>
{
T _store;
public T Get()
{
_store = default(T);
return _store;
}
}
Легко определить, что никакая реализация IFoo<T>
не нарушает ковариацию, потому что у нее ее нет. Все, что необходимо, - это убедиться, что T
не используется в качестве параметра (включая параметр метода установки), и все готово.
Тот факт, что потенциальное ограничение для класса намного сложнее, чем для интерфейса по аналогичным причинам, также снижает степень полезности ковариантных классов. Они, конечно, не были бы бесполезными, но баланс между их полезностью и объемом работы по определению и реализации правил о том, что им разрешено делать, намного меньше, чем баланс полезности ковариантных интерфейсов. над тем, сколько работы было необходимо для их определения и реализации.
Конечно, разницы достаточно, чтобы пройти точку «ну, если вы собираетесь разрешить X, было бы глупо не разрешать Y…».
20.03.2015