Как предотвратить изменение возвращаемой коллекции вызывающим методом? [дубликат]
На этот вопрос уже есть ответ здесь:
У меня есть методы, возвращающие частные коллекции вызывающему объекту, и я хочу запретить вызывающему объекту изменять возвращенные коллекции.
private readonly Foo[] foos;
public IEnumerable<Foo> GetFoos()
{
return this.foos;
}
На данный момент частная коллекция представляет собой фиксированный массив, но в будущем коллекция может превратиться в список, если возникнет необходимость добавления новых элементов во время выполнения.
Существует несколько способов предотвратить изменение коллекции вызывающей стороной. Возврат IEnumerable<T>
является самым простым решением, но вызывающий объект все равно может привести возвращаемое значение к IList<T>
и изменить коллекцию.
((IList<Foo>)GetFoos())[0] = otherFoo;
Клонирование коллекций имеет очевидный недостаток, заключающийся в том, что существуют две коллекции, которые могут развиваться независимо. До сих пор я рассматривал следующие возможности.
- упаковка коллекции в
ReadOnlyCollection<T>
. - возвращает один из итераторов LINQ, определенных классом
Enumerable
, выполняя фиктивную проекцию типаlist.Select(item => item)
. На самом деле я рассматриваю использованиеWhere(item => true)
, потому что возвращаемый итератор кажется более легким. - написание пользовательской оболочки.
Что мне не нравится в использовании ReadOnlyCollection<T>
, так это то, что он реализует IList<T>
и вызов Add()
или доступ к индексатору вызовет исключения. Хотя это абсолютно правильно в теории, почти нет реальных проверок кода IList<T>.IsReadOnly
или IList<T>.IsFixedSize
.
Использование итераторов LINQ-я обернул код в метод расширения MakeReadOnly()
- предотвращает этот сценарий, но он имеет вкус взлома.
Написание пользовательской оболочки? Изобретать велосипед заново?
Какие-либо мысли, соображения или другие решения?
Помечая этот вопрос, я обнаружил этот вопрос переполнения стека, который я раньше не замечал. Джон Скит предлагает использовать " LINQ hack", тоже, но еще более эффективное использование Skip(0)
.
4 ответа:
К сожалению, нет способа достичьточно того, что вы ищете в текущей версии фреймворка. Он просто не имеет понятия индексируемой неизменяемой / доступной только для чтения коллекции как по конкретному типу, так и по стилю интерфейса.
Как вы указали,
ReadOnlyCollection<T>
работает нормально на стороне конкретного типа. Но нет соответствующего интерфейса для реализации, который также статически доступен только для чтения.У тебя есть только один реальный выбор ...
- определите свой собственный класс сборник
- либо только реализовать
IEnumerable<T>
, либо определить необходимый интерфейс только для чтения, который реализует ваша коллекция.
Как насчет создания глубокой копии возвращаемого объекта? [Таким образом, не будет никакого эффекта на исходную коллекцию, даже если вызывающий решит внести изменения в возвращенную копию]
Список и массив имеют метод AsReadOnly:
public IEnumerable<Foo> GetFoos() { return Array.AsReadOnly(this.foos); // or if it is a List<T> // return this.foos.AsReadOnly(); }
В таких случаях я создаю методы в классе для доступа к отдельным элементам частной коллекции. Вы также можете реализовать индексатор (http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx ) о классе, содержащем частную коллекцию.