If I allow your List<Joe> joes
to be generalized as ...
List<Human> humans = joes;
... the two references humans
and joes
are, now onward, pointing to the exact same list. The code following the above assignment has no way of preventing an insertion/addition of an instance of another type of human , say a Plumber, into the list. Given that class Plumber: Human {}
humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe
the list that humans
refers to now contains both joes and plumbers. Note that the same list object is still referred to by the reference joes
. Now if I use the reference joes
to read from the list object I might pop out a plumber instead of a joe. Plumber and Joe are not known to be implicitly interconvertable... so my getting of a plumber instead of a joe from the list breaks down type safety. A plumber is certainly not welcome through a reference to a list of joes.
However in the recent versions of C# , its kind of possible to work around this limitation for a generic class/collection by implementing a generic interface whose type parameter has an out
modifier on it. Say we now have ABag<T> : ICovariable<out T>
. The out modifier restricts the T to ouput positions only (e.g. method return types). You cannot enter any T into the bag. You can only read them out of it. This allows us to generalize joes to an ICovariable<Human>
without worrying about inserting a Plumber into it as the interface doesnt allow that. We can now write ...
ICovariable<Human> humans = joes ; // now its good !
humans.Add(new Plumber()); // error