Как создать неизменяемый класс?

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

У меня есть список элементов в классе.
Хотя если свойство доступно только для чтения, список может быть изменен.

Воздействие IEnumerable в списке делает его неизменным.
Я хотел знать, каковы основные правила, которые нужно соблюдать, чтобы сделать класс неизменным?

Ответ 1

Я думаю, что ты на правильном пути -

  • вся информация, введенная в класс, должна быть указана в конструкторе
  • все свойства должны быть только для геттеров
  • если коллекция (или массив) передается в конструктор, она должна быть скопирована, чтобы запретить ее модификатору позже
  • если вы собираетесь вернуть свою коллекцию, верните копию или версию для чтения (например, используя ArrayList.ReadOnly или аналогичный - вы можете объединить это с предыдущей точкой и сохранить копию только для чтения, которая будет возвращена, когда вызывающие абоненты обратятся к ней), вернуть перечислитель или использовать другой метод/свойство, позволяющий доступ только для чтения в коллекцию
  • Имейте в виду, что вы все еще можете иметь вид изменяемого класса, если какой-либо из ваших членов изменен - ​​если это так, вы должны скопировать все состояние, которое вы хотите сохранить, и не возвращать все изменяемые объекты, кроме вы копируете их, прежде чем возвращать их вызывающему абоненту. Другой вариант - вернуть только неизменные "разделы" изменяемого объекта - благодаря @Brian Расмуссену, чтобы побудить меня расширить эту точку.

Ответ 2

Чтобы быть неизменными, все ваши свойства и поля должны быть только для чтения. И элементы в любом списке сами должны быть неизменными.

Вы можете сделать свойство списка только для чтения следующим образом:

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

}

Ответ 3

Кроме того, имейте в виду, что:

public readonly object[] MyObjects;

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

Ответ 4

Используйте класс 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; }
}

Ответ 5

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