Ну, главная проблема в том, что если у вас есть иерархия классов, например:
class Foo { .. }
class Bar : Foo { .. }
И у вас есть IEnumerator<Bar>
, вы не можете использовать его как IEnumerator<Foo>
, хотя это было бы совершенно безопасно. В 3.5 это вызывает большое количество болезненных вращений. Эта операция всегда была бы безопасной, но она отклонена системой типов, потому что она не знает о ковариантном использовании параметра универсального типа. IEnumerator<Bar>
может вернуть только Bar
, а каждое Bar
- это Foo
.
Аналогично, если у вас есть IEqualityComparer<Foo>
, его можно использовать для сравнения любой пары объектов типа Foo
, даже если один или оба являются Bar
, но его нельзя преобразовать в IEqualityComparer<Bar>
, потому что он не знает о контравариантном использовании универсального параметра типа. IEqualityComparer<Foo>
потребляет только объекты типа Foo
, и каждый Bar
является Foo
.
Без этих ключевых слов мы вынуждены предполагать, что универсальный аргумент может встречаться как в качестве аргумента метода, так и в качестве типа результата метода, и поэтому мы не можем безопасно разрешить любое из приведенных выше преобразований.
С их помощью система типов может позволить нам безопасно повышать или понижать скорость между этими интерфейсами в направлении, указанном ключевым словом, и мы получаем ошибки, указывающие, когда мы нарушим дисциплину, необходимую для обеспечения этой безопасности.