I noticed something strange and there is a possibility I am wrong.
I have an interface IA and class A:
interface IA { .... }
class A : IA { .... }
In other class I have this:
private IList<A> AList;
public IList<IA> {
get { return AList; }
}
But I get compilation error.
But if I change it to:
public IList<IA> {
get { return AList.ToArray(); }
}
Everything is fine.
Why is it?
Why this doesn’t work
Exposing the property as
IList<IA>would allow you to try to addclass B : IAto the list, but the underlying list is reallyIList<A>,Bis notA, so this would blow up in your face. Thus, it is not allowed.Why this works:
Array variance is broken. You can return the list as an array, it will still blow up in your face at runtime if you tried an
Addoperation (or try to replace an object at a given index with something other than an object of typeA, but it’s legal at compile time. A different example of this variance at play:From comments:
If consumers only need to iterate over the sequence and not have random, indexed access to it, you can expose the property as an
IEnumerable<IA>.(The
Selectis actually not technically needed, but using this will prevent consumers from being able to cast the result to its true underlyingList<>type.) If the consumers decide they want a list or an array, they are free to callToList()orToArray()on it, and whatever they do with it (in terms of adding, removing, replacing items) will not affect your list. (Changes to the items’ properties would be visible.) Similarly, you could also expose the collection anIList<IA>yourself in a safe wayAgain, this would return a copy of the list, so any changes to it would not affect your underlying list.