Как запретить вызывающему методу изменять возвращаемую коллекцию?[дубликат]

StackOverflow https://stackoverflow.com/questions/1219268

Вопрос

На этот вопрос уже есть ответ здесь:

У меня есть методы, возвращающие частные коллекции вызывающему объекту, и я хочу запретить вызывающему объекту изменять возвращаемые коллекции.

private readonly Foo[] foos;

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

На данный момент частная коллекция представляет собой фиксированный массив, но в будущем коллекция может стать списком, если возникнет необходимость добавления новых элементов во время выполнения.

Существует несколько решений, позволяющих запретить модификацию коллекции вызывающей стороной.Возвращение IEnumerable<T> является самым простым решением, но вызывающая сторона все равно может преобразовать возвращаемое значение в IList<T> и изменить коллекцию.

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

Клонирование коллекций имеет очевидный недостаток: есть две коллекции, которые могут развиваться независимо.Пока я рассмотрел следующие варианты.

  1. Упаковываем коллекцию в ReadOnlyCollection<T>.
  2. Возврат одного из итераторов LINQ, определенных Enumerable класс, выполнив фиктивную проекцию, например list.Select(item => item).На самом деле я рассматриваю возможность использования Where(item => true) потому что возвращенный итератор кажется более легким.
  3. Написание пользовательской оболочки.

Что мне не нравится в использовании ReadOnlyCollection<T> заключается в том, что он реализует IList<T> и звоню Add() или доступ к индексатору вызовет исключения.Хотя в теории это абсолютно правильно, реальных проверок кода почти нет. IList<T>.IsReadOnly или IList<T>.IsFixedSize.

Использование итераторов LINQ. Я обернул код в метод расширения. MakeReadOnly() - предотвращает этот сценарий, но имеет привкус взлома.

Написание пользовательской оболочки?Изобретать велосипед заново?

Есть какие-нибудь мысли, соображения или другие решения?


Отмечая этот вопрос, я обнаружил этот вопрос о переполнении стека Я раньше не замечал.Джон Скит также предлагает использовать «хак LINQ», но еще эффективнее использовать Skip(0).

Это было полезно?

Решение

К сожалению, нет способа достичь точно то, что вы ищете в текущей версии фреймворка.Он просто не имеет понятия индексируемой неизменяемой/доступной только для чтения коллекции как для конкретного типа, так и для стиля интерфейса.

Как вы отметили, ReadOnlyCollection<T> работает нормально на стороне конкретного типа.Но не существует соответствующего интерфейса для реализации, который также статически доступен только для чтения.

Ваш единственный реальный выбор - это...

  • Определите свой собственный класс коллекции
  • Либо только реализовать IEnumerable<T> или определите необходимый интерфейс только для чтения, который реализует ваша коллекция.

Другие советы

Как насчет создания глубокой копии возвращаемого объекта?[Поэтому исходная коллекция не окажет никакого влияния, даже если вызывающая сторона решит внести изменения в возвращаемую копию]

List и Array имеют метод 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) в классе, содержащем частную коллекцию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top