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

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

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

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

5 ответов

Решение

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

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

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

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

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

}

Также имейте в виду, что:

public readonly object[] MyObjects;

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

Все, что вам нужно, это L... Ehm recordи C# 9.0 или новее.

public record Customer(string FirstName, string LastName, IEnumerable<string> Items);

//...

var person = new Customer("Test", "test", new List<string>() { "Test1", "Test2", "Test3" });
// you can't change anything within person variable
// person.FirstName = "NewName";

Это переводится в неизменяемый класс под названием Customer с тремя свойствами: FirstName, LastName и Items.

Если вам нужна неизменяемая (доступная только для чтения) коллекция в качестве свойства класса, лучше предоставить ее как IEnumerable<T> или же ReadOnlyCollection<T> чем что-то из System.Collections.Immutable

Использовать 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; }
}

Использование ReadOnlyCollection ограничит возможность клиента изменять его.

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

Исходные вопросы и ответы датируются концом 2008 года, теперь существует пространство имен System.Collections.Immutable, которое, как мне кажется, восходит к самому раннему.NET Core (1.0) . Пространство имен по-прежнему недоступно в.NET Standard (текущая версия 2.1) и.NET framework (текущая версия 4.8). В этом пространстве имен есть множество неизменяемых коллекций, включая ImmutableList, о котором спрашивают в исходном вопросе. Однако я считаю, что пространство имен System.Collections.Immutable может появиться в.NET 5, который на данный момент находится в кандидате на выпуск 2.

Кроме того, начиная с C# 6, вы можете иметь неизменяемые автоматически реализуемые свойства, используя только {get; }.

Другие вопросы по тегам