Возврат неизменной коллекции, когда элементы должны быть изменяемыми изначально - PullRequest
1 голос
/ 20 января 2010

Один пример общего случая:

public class Validator
{
   // ...
   public ReadOnlyCollection<InvalidContainer> ContainersUnderMinimum
   {
      get { return _containersUnderMinimum.AsReadOnly(); }
   }
}

public class InvalidContainer
{
   // ...
   public int LowestVolume { get; set; }
}

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

Проблема заключается в том, что Validator хочет возвращать коллекцию неизменяемых объектов только для чтения, но объекты (InvalidContainers) должны быть изменяемыми после построения, чтобы значения (по сути) могли накапливаться (*).

Рефакторинг для использования интерфейса (IInvalidContainer) вызывает головную боль, поскольку общие коллекции не могут быть преобразованы в коллекции базового типа.

Какие есть хорошие модели или методы для решения этой проблемы?

РЕДАКТИРОВАТЬ: Чтобы уточнить, намерение состоит в том, чтобы значение свойства (коллекция) было неизменным. Я понимаю, что ReadOnlyCollection обеспечивает неизменность только коллекции, а не элементов коллекции. Обычно я делаю предметы неизменяемыми, но я не могу в этом (и подобных) случаях. Однако я хочу, чтобы элементы мутировали только во время создания класса Validator. Предотвращение вызывающих абонентов от неразумного кастинга не является целью дизайна; цель состоит в том, чтобы избежать соблазнения вызывающих абонентов с помощью настраиваемого открытого свойства.

РЕДАКТИРОВАТЬ: изменил название для ясности.

РЕДАКТИРОВАТЬ: Вот реорганизованная версия (на основе предложений от Л.Бушкина и рекурсивный):

public IEnumerable<IInvalidContainer> ContainersUnderMinimum
{
   get
   {
      return _containersUnderMinimum.Cast<IInvalidContainer>();
   }
}

Ответы [ 4 ]

2 голосов
/ 20 января 2010

Если я правильно понимаю вашу проблему, вы хотите вернуть коллекцию неизменяемых типов, но внутренне сохраните изменчивую коллекцию. Типичный способ сделать это - создать базовый тип (или интерфейс) для вашего типа, который является неизменным, и вернуть его.

Вы можете либо приводить предметы к этому типу (слабая форма контроля неизменности), либо создавать объекты-обертки и возвращать их (сильный тип управления). Создание объектов Wrapper может быть более дорогим, но они не позволяют внешнему коду просто выполнять приведение типов для обхода неизменности. Кстати, это механизм, который ReadOnlyCollection<T> использует для возврата неизменяемых коллекций.

Чтобы преодолеть тот факт, что типы коллекций имеют ограниченную поддержку приведения, вы можете использовать LINQ для возврата неизменяемой копии неизменяемого типа:

_containersUnderMinimum.Cast<IInvalidContainer>().ToList().AsReadOnly()

Это создает копию коллекции - но она может быть достаточно хорошей - если вам не нужна коллекция для отражения изменений во время выполнения.

Также следует помнить, что ReadOnlyCollection не требует (или не обеспечивает) неизменности элементов коллекции. Скорее, он не позволяет получателю добавлять или удалять элементы - изменение существующих элементов в коллекции все еще возможно.

1 голос
/ 26 декабря 2013

Если ваши изменяемые объекты могут быть изменены только с помощью методов, я бы посоветовал вам включить в свой изменяемый тип ссылку, которая, если она не равна нулю, идентифицирует экземпляр неизменяемого типа, который инкапсулирует те же данные. Ваш изменяемый тип должен включать метод для создания неизменяемой копии; этот метод должен создавать и кэшировать новый неизменный объект, если он еще не содержит ссылку на него. В противном случае он должен вернуть кешированную ссылку. Любой мутирующий метод должен сделать недействительной ссылку на неизменяемый объект. Используя такой подход, нужно избегать повторных копий объектов, которые никогда не были видоизменены.

1 голос
/ 20 января 2010

На самом деле, можно отливать родовые коллекции:

ReadOnlyCollection<IInvalidContainer> result = 
   _containersUnderMinimum.Cast<IInvalidContainer>().ToList().AsReadOnly();

Однако это не мешает потребителю отбрасывать элементы обратно.

0 голосов
/ 20 января 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...