Well, the main problem is that if you have a class hierarchy like:
class Foo { .. }
class Bar : Foo { .. }
And you have an IEnumerator<Bar>
, you can't use that as an IEnumerator<Foo>
even though that would be perfectly safe. In 3.5 this forces a large number of painful gyrations. This operation would always be safe but is denied by the type system because it doesn't know about the covariant use of the generic type parameter. IEnumerator<Bar>
can only return a Bar
and every Bar
is a Foo
.
Similarly, if you had an IEqualityComparer<Foo>
it can be used to compare any pair of objects of type Foo
even if one or both is a Bar
, but it cannot be cast into an IEqualityComparer<Bar>
because it doesn't know about the contravariant use of the generic type parameter. IEqualityComparer<Foo>
only consumes objects of type Foo
and every Bar
is a Foo
.
Without these keywords we're forced to assume the generic argument can occur as both an argument to a method and as the result type of a method, and so we can't safely allow either of the above conversions.
With them, the type system is free to allow us to safely upcast and downcast between those interfaces in the direction indicated by the keyword and we get errors indicating when we would violate the discipline required to ensure that safety.