Вопрос

У меня есть объект в многопоточной среде, который поддерживает сбор информации, например:

public IList<string> Data 
{
    get 
    {
        return data;
    }
}

в настоящее время у меня есть return data; обернутый ReaderWriterLockSlim для защиты коллекции от нарушений обмена.Однако, чтобы быть вдвойне уверенным, я бы хотел вернуть коллекцию как доступную только для чтения, чтобы вызывающий код не мог вносить изменения в коллекцию, а только просматривать то, что там уже есть.Это вообще возможно?

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

Решение

Если ваша единственная цель — заставить вызывающий код не допустить ошибки и изменить коллекцию, когда она должна только читать, все, что необходимо, — это вернуть интерфейс, который не поддерживает добавление, удаление и т. д.Почему бы не вернуться IEnumerable<string>?Вызывающий код придется выполнить, что они вряд ли сделают, не зная внутреннего устройства свойства, к которому они обращаются.

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

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

Если ваши базовые данные хранятся в виде списка, вы можете использовать Список(T).AsReadOnly метод.
Если ваши данные можно перечислить, вы можете использовать Перечисляемый.ToList метод для приведения вашей коллекции к List и вызова для нее AsReadOnly.

Я проголосовал за принятый вами ответ и согласен с ним, однако могу ли я дать вам кое-что для размышления?

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

Основное преимущество этого заключается в том, что вы не можете добавлять код в коллекции, поэтому, когда у вас есть собственная «коллекция» в вашей объектной модели, у вас ВСЕГДА есть код поддержки, не относящийся к объектно-ориентированному дизайну, распределенный по всему проекту для доступа к ней.

Например, если бы ваша коллекция состояла из счетов-фактур, у вас, вероятно, было бы 3 или 4 места в вашем коде, где вы перебирали бы неоплаченные счета.У вас может быть метод getUnpaidInvoices.Однако реальная сила проявляется, когда вы начинаете думать о таких методах, как «payUnpaidInvoices(плательщик, учетная запись);».

Когда вы передаете коллекции вместо написания объектной модели, вам никогда не придет в голову рефакторинг целых классов.

Также обратите внимание, что это делает вашу задачу особенно приятной.Если вы не хотите, чтобы люди меняли коллекции, ваш контейнер не должен содержать мутаторов.Если позже вы решите, что хотя бы в одном случае вам действительно НЕОБХОДИМО его изменить, вы можете создать для этого безопасный механизм.

Как вы решаете эту проблему, когда передаете собственную коллекцию?

Кроме того, собственные коллекции нельзя пополнить дополнительными данными.Вы поймете это в следующий раз, когда обнаружите, что передаете (Collection, Extra) более чем одному или двум методам.Это указывает на то, что «Extra» принадлежит объекту, содержащему вашу коллекцию.

Мне кажется, вы здесь путаете понятия.

А ReadOnlyCollection предоставляет оболочку только для чтения для существующей коллекции, позволяя вам (класс A) передавать ссылку на коллекцию, зная, что вызывающая сторона (класс B) не может изменить коллекцию (т. е.не могу добавлять или удалять любые элементы из коллекции.)

Нет абсолютно никаких гарантий потокобезопасности.

  • Если вы (класс А) продолжите изменять базовую коллекцию после того, как раздали ее в качестве ReadOnlyCollection тогда класс B увидит эти изменения, все итераторы будут признаны недействительными и т. д.и, как правило, быть открытым для любых обычных проблем параллелизма с коллекциями.
  • Кроме того, если элементы в коллекции изменяемы, вы оба (класс A) и вызывающий объект (класс B) сможет изменить любое изменяемое состояние объектов в коллекции.

Ваша реализация зависит от ваших потребностей:- Если вас не волнует, что вызывающий абонент (класс B) увидит какие-либо дальнейшие изменения в коллекции, вы можете просто клонировать коллекцию, раздать ее и перестать беспокоиться.- Если вам определенно нужно, чтобы вызывающая сторона (класс B) видела изменения, внесенные в коллекцию, и вы хотите, чтобы это было потокобезопасным, тогда у вас возникает большая проблема.Одна из возможностей — реализовать собственный потокобезопасный вариант ReadOnlyCollection, чтобы разрешить заблокированный доступ, хотя это будет нетривиально и непроизводительно, если вы хотите поддерживать IEnumerable, и это все еще не защитит вас от изменяемых элементов в коллекции.

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

Вы хотите использовать урожай ключевое слово.Вы просматриваете список IEnumerable и возвращаете результаты с помощью YEILD.Это позволяет потребителю использовать for каждого без изменения коллекции.

Это будет выглядеть примерно так:

List<string> _Data;
public IEnumerable<string> Data
{
  get
  {
    foreach(string item in _Data)
    {
      return yield item;
    }
  }
}

Вместо этого вы можете использовать копию коллекции.

public IList<string> Data {
get {
    return new List<T>(data);
}}

Таким образом, не имеет значения, обновляется ли он.

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