3

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).

Community
  • 1
  • 1
Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • 1
    Unless there are security issues (potentially hostile callers), I generally don't worry about protecting against unwise casting or calling Add on a ReadOnlyCollection. Foolish developers can use reflection and access internals, too. It's difficult to make code idiot-proof. The goal is to protect against innocent use of the public interface. – TrueWill Jan 22 '10 at 20:56

4 Answers4

5

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
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 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 Aug 18 '09 at 14:52
  • @DanielBrückner: What real advantage does that have over wrapping in a `ReadOnlyCollection` and casting to `IEnumerable`? The fact that the resulting object can be cast to `IList` will enormously improve the performance of some Linq methods like `Count` and `Last`. – supercat Nov 27 '12 at 21:37
0

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
  • 6,372
  • 9
  • 39
  • 59
  • 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 Aug 02 '09 at 23:06
0

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
  • 286,951
  • 70
  • 623
  • 758
  • This methods return the collection wrapped in a ReadOnlyCollection and I don't like that this class implements IList as described in the question. – Daniel Brückner Aug 02 '09 at 23:09
0

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
  • 180
  • 1
  • 12
  • 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 - 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 Aug 02 '09 at 23:18
  • 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 Aug 03 '09 at 05:30