2008-09-10 3 views
13

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

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

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

+0

Возможно, вы также захотите увидеть [readonlycollection-or-ienumerable-for-exposing-members-collections /] (http://stackoverflow.com/questions/491375/readonlycollection-or-ienumerable-for-exposing-member- коллекции/491591 # 491591) – nawfal 2013-11-11 11:02:39

ответ

16

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

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

+0

IEnumerable звучит как хороший путь вперед на самом деле. Как вы полагаете, я намерен только защитить коллекцию от прямой модификации; члены коллекции не будут изменены (или должны быть изменены). – alastairs 2008-09-11 09:17:40

28

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

3

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

10

Я думаю, вы вводите в заблуждение понятия здесь.

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

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

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

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

+0

Отличная точка! +1 – nawfal 2013-11-11 11:41:58

2

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

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

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

2

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

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

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

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

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

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

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

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

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

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

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

+1

Отличный ответ, хотелось бы, чтобы я голосовал более одного раза! – alastairs 2009-12-23 12:15:16

Смежные вопросы