问题
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.
- Wrapping the collection in
ReadOnlyCollection<T>
. - Returning one of the LINQ iterators defined by the
Enumerable
class by performing a dummy projection likelist.Select(item => item)
. Actually I consider usingWhere(item => true)
because the returned iterator seems more lightweight. - 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)
.
回答1:
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.
回答2:
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]
回答3:
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();
}
回答4:
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.
来源:https://stackoverflow.com/questions/1219268/how-to-prevent-a-method-caller-from-modifying-a-returned-collection