Posted on 29. September 2021

C# 9 средства доступа и записи init

Это третья статья из серии статей о C# 9. В предыдущих статьях рассмотрели программы верхнего уровня и целевые типизированные выражения, новые функции в подборке паттернов, а также новые возможности C# 9 для методов и функций.

В этой статье рассмотрим средства доступа и записи инициализации.

Аксессор Init

В C# 9 появился новый метод доступа к свойствам с именем init. Аксессор  init работает как public set  метод доступа, но его можно использовать только во время инициализации объекта.  Попытка установить свойство в другом месте дает ошибку компиляции:

public class Person

{

 public string FirstName { get; init; }

 public string LastName { get; init; }

}

 

Person person = new()

{

  FirstName = "John"       // OK.

};

 

person.FirstName = "Jane"; // error CS8852.

 

В конструкторе можно использовать аксессоры init. И для метода доступа может быть предоставлено тело, которое может изменять поля  readonly:

public class Person
{
 private readonly string _firstName;
 public Person(string firstName)
 {
   FirstName = firstName;
 }
 public string FirstName
 {
   get => _firstName;
   init =>
      _firstName = value ?? throw new ArgumentNullException(nameof(value));
 }
 public string LastName { get; init; }
}

 

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

Записи

Классы C# являются ссылочными типами. Это означает, что переменные не являются экземплярами типа, а являются ссылками на экземпляр этого типа (который находится в управляемой куче). Реализация равенства по умолчанию для ссылочных типов заключается в проверке, ссылаются ли две переменные на один и тот же экземпляр:

class Person { public string FirstName { get; set; }};
 
Assert.False(new Person { FirstName = "John" }
         .Equals(new Person { FirstName = "John" }));

 

При работе с объектами, представляющими данные, может быть полезно сравнить два разных экземпляра и посмотреть, имеют ли они одинаковые значения. Именно это и позволяют новые записи C# 9:

record Person { public string FirstName { get; set; }};
 
Assert.True(new Person { FirstName = "John" }
         .Equals(new Person { FirstName = "John" }));

 

Изменили Person с class на record. Теперь два разных экземпляра с одинаковыми значениями считаются равными.

Person record это класс, для которого компилятор создал реализацию IEquatable и GetHashCode, основанную на полях типа. Сделать это самостоятельно в class возможно, но это утомительно и чревато ошибками.

Компилятор также сгенерирует метод ToString, который возвращает строку с именем типа, значениями общедоступных полей и значениями свойств, доступными для чтения.

Вы можете добавлять в record конструкторы и другие члены, как и для обычного  class.

Записи поддерживают наследование с использованием обычного синтаксиса: :

record Student : Person
{
 public int ID { get; set; }
}

 

Реализация равенства учитывает тип среды выполнения: экземпляр Person никогда не будет равен экземпляру  Student.

Записи C # могут использоваться с аксессорами init, что обеспечивает удобный способ создания типов, которые нельзя изменить после их создания (неизменяемые типы):

public record Person
{
 public string? FirstName { get; init; }
 public string? LastName { get; init; }
}

 

При работе с неизменяемыми данными новые значения часто создаются путем изменения существующих. Записи поддерживают это с помощью выражения  with :

Person jane = person with
{
 FirstName = "Jane"
};

 

Выражение with копирует значения из экземпляра и позволяет изменять его с помощью средств доступа init. Копирование основывается на конструкторе копирования, который создается для типов record.

Однострочник позволяет вам создать неизменяемую запись с помощью конструктора и метода деконструкции:

record Person(string FirstName, string LastName);
 
var person = new Person("John", "Doe");
(string firstName, string lastName) = person;

 

К записи можно добавить тело, чтобы добавить дополнительных членов или уточнить свойства, которые в противном случае были бы автоматически сгенерированы:

record Person(string FirstName, string lastName)
{
 public string LastName { get; } = lastName;
}

 

Заключение

В этой статье мы рассмотрели методы доступа init, которые можно использовать для объявления неизменяемых свойств. Затем мы увидели, как records упрощают создание ссылочных типов с семантикой значений. Комбинируя методы доступа records и init, мы можем создавать неизменяемые модели данных, которые поддерживают изменение с помощью выражения with.

В пятой части серии статей о C # 9 рассматриваются расширенные функции, связанные с внутренним взаимодействием и производительностью.

C# 9 можно использовать с .NET 5 SDK, который доступен в Red Hat Enterprise Linux и Red Hat OpenShift, в Fedora, а также в Microsoft для Windows, macOS и других дистрибутивах Linux.

 

Источник 

 

 



Comments are closed