views:

154

answers:

4

I have methods returning private collections to the caller and I want to prevent the caller from modifying the returned collections.

private readonly Foo[] foos;

public IEnumerable<Foo> GetFoos()
{
    return this.foos;
}

At the moment the private collection is a fixed array, but in the future the collection might become a list if the need for adding new items at run time arises.

There are several solutions to prevent the caller from modifying the collection. Returning IEnumerable<T> is the simplest solution, but the caller can still up-cast the return value to IList<T> and modify the collection.

((IList<Foo>)GetFoos())[0] = otherFoo;

Cloning the collections has the obvious disadvantage that there are two collections that can evolve independently. So far I have considered the following options.

  1. Wrapping the collection in ReadOnlyCollection<T>.
  2. Returning one of the LINQ iterators defined by the Enumerable class by performing a dummy projection like list.Select(item => item). Actually I consider using Where(item => true) because the returned iterator seems more lightweight.
  3. Writing a custom wrapper.

What I don't like about using ReadOnlyCollection<T> is that it implements IList<T> and calling Add() or accessing the indexer will cause exceptions. While this is absolutly correct in theory, almost no real code checks IList<T>.IsReadOnly or IList<T>.IsFixedSize.

Using the LINQ iterators - I wrapped the code in an extension method MakeReadOnly() - prevents this scenario, but it has the taste of a hack.

Writing a custom wrapper? Reinventing the wheel?

Any thoughts, considerations, or other solutions?


While tagging this question, I discovered this Stack Overflow question I didn't notice before. Jon Skeet suggest to use the "LINQ hack", too, but even more efficient using Skip(0).

+3  A: 

Unfortunately there is no way to achieve exactly what you are looking for in the current version of the framework. It simply has no concept of an indexable immutable / read-only collection on both the concrete type and interface style.

As you pointed out, ReadOnlyCollection<T> works OK on the concrete type side. But there is no corresponding interface to implement which is also statically read-only.

You're only real choice is to ...

  • Define your own collection class
  • Either only implement IEnumerable<T> or define a need read-only interface which your collection implements.
JaredPar
I accepted this answer because it clearifies that there is no build-in support. A decided to stick with Enumerable.Skip(0).
Daniel Brückner
A: 

How about making a deep copy of the object being returned? [So there will be no effect on the original collection even if the caller decides to make changes to the copy returned]

Vyas Bharghava
A copy of the collection is not desirable, because the caller would just get a snapshot and wouldn't notice changes made to the collection.
Daniel Brückner
A: 

List and Array have a AsReadOnly method :

public IEnumerable<Foo> GetFoos()
{
    return Array.AsReadOnly(this.foos);
    // or if it is a List<T>
    // return this.foos.AsReadOnly();
}
Thomas Levesque
This methods return the collection wrapped in a ReadOnlyCollection<T> and I don't like that this class implements IList<T> as described in the question.
Daniel Brückner
A: 

In such cases i create a methods insde the class to access individual elements of the private collection. You also can realize indexer (http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx) on the class that contains private collection.

RollingStone
Implementing a method to obtain a single element has at least two disadvantages - a second method or a property to return the number of elements in the collection is required and all the advantages of IEnumerable<T> - foreach and LINQ - are lost. Creating an indexer requires a method or property returning the number of elements, too, and some classes have several methods returning collections. This opts indexers out.
Daniel Brückner
Yes, sure, it depends on your tasks.But you can add a field that returns the number of elements in private collection.You can also create inner class, that will realize access logic.
RollingStone