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
private IList<A> AList;
public IList<IA> { get { return AList; } }
Exposing the property as IList<IA>
would allow you to try to add class B : IA
to the list, but the underlying list is really IList<A>
, B
is not A
, so this would blow up in your face. Thus, it is not allowed.
Why this works:
public IList<IA> { get { return AList.ToArray(); } }
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 Add
operation (or try to replace an object at a given index with something other than an object of type A
, but it's legal at compile time. A different example of this variance at play:
string[] array = new string[10];
object[] objs = array; // legal
objs[0] = new Foo(); // will bite you at runtime
From comments:
So what you suggest to use? How can I make the property return valid object? How can I make the return value read only?
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>
.
public IEnumerable<IA> TheList
{
get { return AList.Select(a => a); }
}
(The Select
is actually not technically needed, but using this will prevent consumers from being able to cast the result to its true underlying List<>
type.) If the consumers decide they want a list or an array, they are free to call ToList()
or ToArray()
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 an IList<IA>
yourself in a safe way
public IList<IA> TheList
{
get { return AList.ToList<IA>(); }
}
Again, this would return a copy of the list, so any changes to it would not affect your underlying list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With