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

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.


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.


This has already been said, but I don't see any of the answers as being superclear.

The easiest way is to simply return a ReadOnlyCollection

private List<object> objs;

public ReadOnlyCollection<object> Objs {
    get {
        return objs.AsReadOnly();
    }
}

The drawback with this is, that if you want to change your implementation later on, then some callers may already be dependent on the fact, that the collection provides random access. So a safer definition would be to just expose an IEnumerable

public IEnumerable<object> Objs { 
    get {
        return objs.AsReadOnly();
    }
}

Note that you don't have to call AsReadOnly() to compile this code. But if you don't, the caller my just cast the return value back to a List and modify your list.

// Bad caller code
var objs = YourClass.Objs;
var list = objs as List<object>;
list.Add(new object); // They have just modified your list.

The same is potential problem also exists with this solution

public IEnumerable<object> Objs { 
    get { 
        return objs.AsEnumerable(); 
    } 
}

So I would definately recommend that you call AsReadOnly() on you list, and return that value.