Read this article in your language IT | EN | DE | ES
Это третья статья из серии статей о 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.
Источник
Exception: Stack empty.