Ответ 1
Ну, здесь есть два вопроса. Во-первых, может ли компилятор всегда это делать? Во-вторых, должен ли он (если он может)?
По первому вопросу, я отдам Эрику Липперту, который сделал этот комментарий, когда я привел именно эту проблему во 2-е издание С# в глубину:
Мне не ясно, что мы разумно могли бы даже, если бы захотели. Мы можем легко подойти с ситуациями, требующими дорогого глобального анализа всех интерфейсов в программе выработать отклонения, и мы можем легко найти ситуации, когда
<in T, out U>
или<out T, in U>
и не может решить между ними. И с плохими производительность и неоднозначные случаи маловероятны.
(Надеюсь, Эрик не возражает против того, чтобы я цитировал этот дословный текст, он ранее очень хорошо делился такими прозрениями, поэтому я иду мимо прошлой формы:)
С другой стороны, я подозреваю, что все еще есть случаи, когда их можно вывести без какой-либо двусмысленности, поэтому вторая точка по-прежнему актуальна...
Я не думаю, что это должно быть автоматическим, даже если компилятор может однозначно знать, что он действителен только одним способом. Хотя расширение интерфейса всегда в какой-то степени является нарушением, обычно это не так, если вы только его реализуете. Однако, если люди полагаются на ваш интерфейс, чтобы быть вариантом, вы не сможете добавлять к нему методы, не разбивая клиентов... даже если они просто звонящие, а не исполнители. Добавленные вами методы могут изменить ранее ковариантный интерфейс, чтобы стать инвариантным, и в этот момент вы разбиваете всех вызывающих, которые пытаются использовать его ковариантно.
В принципе, я считаю, что это нужно, чтобы это было явным - это дизайнерское решение, которое вы должны делать сознательно, а не просто случайно констатировать ковариацию/контравариантность, не подумав об этом.