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


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.