Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow iteration over a private collection but not modification?

If I have the following class member:

private List<object> obs;

and I want to allow traversal of this list as part of the class' interface, how would I do it?

Making it public won't work because I don't want to allow the list to be modified directly.

like image 379
izb Avatar asked Feb 19 '10 16:02

izb


2 Answers

You would expose it as an IEnumerable<T>, but not just returning it directly:

public IEnumerable<object> Objects { get { return obs.Select(o => o); } }

Since you indicated you only wanted traversal of the list, this is all you need.

One might be tempted to return the List<object> directly as an IEnumerable<T>, but that would be incorrect, because one could easily inspect the IEnumerable<T> at runtime, determine it is a List<T> and cast it to such and mutate the contents.

However, by using return obs.Select(o => o); you end up returning an iterator over the List<object>, not a direct reference to the List<object> itself.

Some might think that this qualifies as a "degenerate expression" according to section 7.15.2.5 of the C# Language Specification. However, Eric Lippert goes into detail as to why this projection isn't optimized away.

Also, people are suggesting that one use the AsEnumerable extension method. This is incorrect, as the reference identity of the original list is maintained. From the Remarks section of the documentation:

The AsEnumerable<TSource>(IEnumerable<TSource>) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable<T> to IEnumerable<T> itself.

In other words, all it does is cast the source parameter to IEnumerable<T>, which doesn't help protect referencial integrity, the original reference is returned and can be cast back to List<T> and be used to mutate the list.

like image 178
casperOne Avatar answered Oct 15 '22 00:10

casperOne


You can use a ReadOnlyCollection or make a copy of the List and return it instead (considering the performance penalty of the copy operation). You can also use List<T>.AsReadOnly.

like image 41
bruno conde Avatar answered Oct 14 '22 23:10

bruno conde