Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

list.ToArray vs list

Tags:

c#

.net

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?

like image 794
Naor Avatar asked Jul 19 '11 16:07

Naor


1 Answers

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.

like image 69
Anthony Pegram Avatar answered Oct 09 '22 18:10

Anthony Pegram