Як створити незмінний клас?


113

Я працюю над створенням непорушного класу.
Я позначив усі властивості як лише для читання.

У мене є список предметів у класі.
Хоча, якщо властивість є лише для читання, список можна змінити.

Розкриття IE численного списку робить його незмінним.
Мені хотілося знати, які основні правила потрібно дотримуватися, щоб зробити клас незмінним?


2
Я настійно рекомендую прочитати серію блогу Еріка Ліпперта про незмінність , зокрема запис про "види незмінності" . Ваш коментар про те, що "Викриття IE численного списку робить його непорушним" здається мені дещо дивним. Що ви маєте на увазі під цим?
Джон Скіт

Я мав на увазі, а не дозволяти доступу до списку (Якщо об’єкт має список деяких інших об'єктів), що дозволяє користувачеві отримувати доступ до членів з IEnumerable. Список розмов тут як у конкретному прикладі, але це може бути будь-яка структура даних.
Бісванат

1
Посилання Джона Скіта на серію блогів Еріка Ліпперта зламалися, коли MSDN архівувала ці блоги. Будь ласка, дивіться "види незмінності" . Інша серія, здається, знаходиться в грудні 2007 + січні 2008 року на панелі зліва.
Джон Дой

Будь ласка , дивіться серію блогу Еріка Ліпперта про те , як люди зазвичай плутають терміни atomicity, volatilityі immutability: Частина перша , частина друга , і частина третя . Це з його особистого блогу і, я вважаю, більш привітним для новачків, ніж його повідомлення в MSDN.
Джон Дой

Відповіді:


121

Я думаю, ти на правильному шляху -

  • вся інформація, введена в клас, повинна подаватися в конструкторі
  • всі властивості мають бути лише дітьми
  • якщо колекція (або масив) передається в конструктор, її слід скопіювати, щоб абонент не міг її змінити пізніше
  • якщо ви збираєтеся повернути свою колекцію, поверніть копію або версію, доступну лише для читання (наприклад, використовуючи ArrayList.ReadOnly чи подібні) - ви можете комбінувати це з попереднім пунктом і зберігати копію, що може бути лише для читання абоненти отримують доступ до нього), повертають обчислювач або використовують інший метод / властивість, що дозволяє отримати доступ лише до колекції
  • майте на увазі, що ви все ще можете мати вигляд класу, що змінюється, якщо хтось із ваших членів може змінюватись - якщо це так, вам слід скопіювати будь-який стан, який ви хочете зберегти, і уникати повернення цілих об'єктів, що змінюються, якщо ви не копіюєте їх перш ніж повернути їх абоненту - ще один варіант - повернути лише незмінні "секції" об'єкта, що змінюється, - завдяки @Brian Rasmussen за те, що він заохотив мене розширити цю точку

Чи повинен я написати властивість обгортки шукати властивість, яка дозволяє вам лише шукати вгору? І дякую за відповідь.
Biswanath

5
Будь-який тип змінної посилання, переданий в якості аргументу конструктору, повинен бути скопійований. В іншому випадку абонент все ще матиме посилання на стан.
Брайан Расмуссен

@Brian Rasmussen, ви праві, але навіть якщо це скопійовано, будь-який абонент може отримати доступ до об'єкта, що змінюється, залежно від наданих аксесуарів. У разі передачі зміненого об'єкта класу найкраще завжди повертати іншу копію або незмінні ділянки об'єкта
Блер Конрад

@Blair - Якщо в класі є словник, який я просто хочу використати для пошуку. Чи має бути властивість лише для читання, яка шукає, добре?
Biswanath

@Biswanath - так, із застереженням, що якщо значення в словнику змінні, вам потрібно буде захистити їх перед поверненням, якщо ви не хочете, щоб вони були мутованими
Блер Конрад

18

Щоб бути непорушними, усі ваші властивості та поля повинні бути прочитані лише заново. І елементи будь-якого списку повинні самі бути незмінними.

Ви можете створити властивість списку лише для читання таким чином:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}

1
Я думаю, що ImmutableList було б краще.
Кевін Вонг

використовувати ImmutableList, також розглянути питання про герметизацію класу
kofifus

Я не впевнений , що ImmutableList добре підходить для цього випадку використання: basic rules to make a class (that contains a list) immutable. Семантика ImmutableList дозволяє більш ефективно використовувати пам'ять при додаванні елементів до копії списку, але це, здається, є більш конкретним випадком використання.
Джо

11

Також пам’ятайте, що:

public readonly object[] MyObjects;

не є незмінним, навіть якщо він позначений ключовим словом для читання Ви все ще можете змінити окремі посилання / значення масиву за допомогою індексу.


5

Використовуйте ReadOnlyCollectionклас. Він розташований у System.Collections.ObjectModelпросторі імен.

У будь-якому випадку, що повертає ваш список (або в конструкторі), встановіть його як колекцію лише для читання.

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}

1

Іншим варіантом буде використання шаблону відвідувачів, а не відкриття будь-яких внутрішніх колекцій.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.