Как предоставить доступ к свойству коллекции?[закрыто]

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

  •  09-06-2019
  •  | 
  •  

Вопрос

Каждый раз, когда я создаю объект, имеющий свойство collection, я размышляю о том, как лучше всего это сделать?

  1. общедоступное свойство с получателем, который возвращает ссылку на закрытую переменную
  2. явные get_ObjList и set_ObjList методы, которые возвращают и создают новые или клонированные объекты каждый раз
  3. явный get_ObjList, который возвращает IEnumerator и set_ObjList, который принимает IEnumerator

Имеет ли значение, является ли коллекция массивом (т. Е. objList.Clone()) по сравнению со списком?

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

И разве варианты 2 и 3 не нарушают сериализацию?Является ли это уловкой 22 или вам нужно реализовывать пользовательскую сериализацию всякий раз, когда у вас есть свойство collection?

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

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

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

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

Решение

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

1) Если пользователи будут добавлять и удалять элементы из коллекции объекта, то лучше всего использовать простое свойство коллекции, доступное только для получения (вариант № 1 из исходного вопроса):

private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
  get { return this.myCollection_; }
}

Эта стратегия используется для Items коллекции в WindowsForms и WPF ItemsControl элементы управления, в которых пользователи добавляют и удаляют элементы, которые они хотят, чтобы отображался элемент управления.Эти элементы управления публикуют фактическую коллекцию и используют обратные вызовы или прослушиватели событий для отслеживания элементов.

WPF также предоставляет некоторые настраиваемые коллекции, позволяющие пользователям отображать коллекцию элементов, которыми они управляют, например ItemsSource собственность на ItemsControl (вариант №3 из первоначального вопроса).Однако это не самый распространенный вариант использования.


2) Если пользователи будут считывать только данные, поддерживаемые объектом, то вы можете использовать коллекцию, доступную только для чтения, как Спорный вопрос предложенный:

private readonly List<T> myPrivateCollection_ = new ...;
private ReadOnlyCollection<T> myPrivateCollectionView_;
public ReadOnlyCollection<T> MyCollection {
  get {
    if( this.myPrivateCollectionView_ == null ) { /* lazily initialize view */ }
    return this.myPrivateCollectionView_;
  }
}

Обратите внимание , что ReadOnlyCollection<T> обеспечивает просмотр базовой коллекции в реальном времени, поэтому вам нужно создать представление только один раз.

Если внутренняя коллекция не реализует IList<T>, или, если вы хотите ограничить доступ для более продвинутых пользователей, вы можете вместо этого перенести доступ к коллекции через перечислитель:

public IEnumerable<T> MyCollection {
  get {
    foreach( T item in this.myPrivateCollection_ )
      yield return item;
  }
}

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


3) Наконец, если вам нужно предоставить массивы, а не коллекции более высокого уровня, то вам следует вернуть копию массива, чтобы пользователи не могли его изменять (вариант № 2 из исходного вопроса):

private T[] myArray_;
public T[] GetMyArray( ) {
  T[] copy = new T[this.myArray_.Length];
  this.myArray_.CopyTo( copy, 0 );
  return copy;
  // Note: if you are using LINQ, calling the 'ToArray( )' 
  //  extension method will create a copy for you.
}

Вам не следует предоставлять доступ к базовому массиву через свойство, так как вы не сможете определить, когда пользователи изменят его.Чтобы разрешить изменение массива, вы можете либо добавить соответствующий SetMyArray( T[] array ) метод или используйте пользовательский индексатор:

public T this[int index] {
  get { return this.myArray_[index]; }
  set {
    // TODO: validate new value; raise change event; etc.
    this.myArray_[index] = value;
  }
}

(конечно, реализуя пользовательский индексатор, вы будете дублировать работу классов BCL :)

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

Обычно я использую публичный метод получения System.Collections.ObjectModel.ReadOnlyCollection:

public ReadOnlyCollection<SomeClass> Collection
{
    get
    {
         return new ReadOnlyCollection<SomeClass>(myList);
    }
}

И публичные методы для объекта, чтобы изменить коллекцию.

Clear();
Add(SomeClass class);

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

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

Почему вы предлагаете использовать ReadOnlyCollection (T) как компромисс? Если вам все еще нужно получать уведомления об изменениях, сделанные в оригинальном упакованном IList, вы также можете использовать ReadOnlyObservableCollection (T) , чтобы обернуть вашу коллекцию. Будет ли это меньшим компромиссом в вашем сценарии?

Я разработчик Java, но я думаю, что то же самое для C #.

Я никогда не выставляю свойство частной коллекции, потому что другие части программы могут изменить его, не обращая на это внимания родителя, поэтому в методе getter я возвращаю массив с объектами коллекции, а в методе setter я вызываю clearAll() над коллекцией, а затем addAll()

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

Я рекомендую использовать новый IReadOnlyList<T> и IReadOnlyCollection<T> Интерфейсы для предоставления доступа к коллекции (требуется .NET 4.5).

Пример:

public class AddressBook
{
    private readonly List<Contact> contacts;

    public AddressBook()
    {
        this.contacts = new List<Contact>();
    }

    public IReadOnlyList<Contact> Contacts { get { return contacts; } }

    public void AddContact(Contact contact)
    {
        contacts.Add(contact);
    }

    public void RemoveContact(Contact contact)
    {
        contacts.Remove(contact);
    }
}

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

Избегать использование интерфейса IEnumerable<T> чтобы предоставить доступ к коллекции.Этот интерфейс не определяет никакой гарантии того, что несколько перечислений выполняются хорошо.Если IEnumerable представляет запрос, то каждое перечисление выполняет запрос снова.Разработчики, которые получают экземпляр IEnumerable, не знают, представляет ли он коллекцию или запрос.

Подробнее об этой теме можно прочитать здесь Вики-страница.

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