then one could cast to IList and add items not of the correct type.
This is only a partial justification: It is well possible to implement IList
in a way so no items of an incorrect type can be added - as evidenced by the implementation that is List<T>
.
However,, List<T>
already exists as a part of the base class library. For our convenience, it implements IList
and throws exceptions. If we want to use its IList
implementation, we can do so within these constraints, and if we do not want to use it, we have no further work.
In contrast to this, IList<T>
is an interface. If it inherited from IList
, every implementor of IList<T>
would have to implement all of the weakly-typed methods of IList
, adding a lot of work that is often not desired.
Moreover, note that List<T>
implements IList
expilcitly. That means, the public interface of List<T>
does not grow; you only get the IList
methods if you explicitly cast to IList
. In an interface, that is not possible, as an interface cannot enforce explicit implementation of another interface.