У меня есть методы, возвращающие частные коллекции вызывающей стороне, и я хочу, чтобы вызывающая сторона не изменяла возвращаемые коллекции.
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)
.