Ayende: Covariance and generic return types.
October 24, 2005
Ayende has posted up a question about why the following code won’t work:
1 public IList<object> GetList()
2 {
3 return new List<string>();
4 }
The issue here is that List<string> does not implement IList<object>, instead it implements IList<string>. We can tell this by looking at the class definition:
1 public class List<T> :
2 IList<T>,
3 ICollection<T>,
4 IEnumerable<T>,
5 IList,
6 ICollection,
7 IEnumerable
8 {
9 ...
10 }
The issue becomes apparent when you do the substitution yourself and compare what the generated type would look like in each case:
1 List<string> stringList = new List<string>();
2 public class List<string> :
3 IList<string>,
4 ICollecftion<string>,
5 IEnumerable<string>,
6 IList,
7 ICollection,
8 IEnumerable
9 {
10 ...
11 }
12
13 List<object> objectList = new List<object>();
14 public class List<object> :
15 IList<object>,
16 ICollecftion<object>,
17 IEnumerable<object>,
18 IList,
19 ICollection,
20 IEnumerable
21 {
22 ...
23 }
You can clearly see here that one list implements IList<string> whereas the other implements IList<object>. The interesting thing here is that because both System.String and System.Object are reference types – they both end up sharing the same constructed type at the runtime level, but the runtime enforces some rules – for our own safety.
If you take a look at the inheritence hierarchy for IList<T> then you can see that the first time you have a common concrete class is IEnumerable – so in Ayende’s case he would need to return an IEnumerable for the code to compile.

The thing about IEnumerable is that using that interface, you can’t add any items to the list. Thats just as well, because if you were able to get an IList<object> back and add an item to it, what would happen? Well, because the constructed type is List<string> it means that the code is actually incapable of accepting an object – so the interface that List<object> would expose would essentially be a lie.
So this is a really good example of where a static type system can save you from yourself, and generics are really there to enable your code to be flexible whilst at the same time be statically verifyable. This behaviour is by design.
October 24, 2005 at 12:00 am
It’s annoying because I had a list of derived type that I needed to exposed as the base type…
Wesner Moise replied to my post about it with the same answer, but yours actually explain things.
I agree to the design decision, although staring at the compiler error isn’t fun.
There are some cases where the generic type of a derived class can be treated as a generic type of a base class, but the compiler has no way of knowing that…