Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Подсказка
Вы новичок в разработке программного обеспечения? Сначала начните с учебников для начинающих. Вы встретите записи, когда вам нужны сжатые типы данных со встроенным равенством.
Есть опыт на другом языке? Записи C# похожи на классы данных в Kotlin или классах регистра в Scala. Они оптимизированы для хранения данных, с равенством, генерируемым компилятором, и семантикой копирования. Просмотрите разделы record class vs record struct и выражений with для шаблонов, специфичных для C#, чтобы найти характерные шаблоны.
Ключевое слово record является модификатором, применяемым к class или struct. Он сообщает компилятору создать равенство значений, отформатированную ToStringи недеструктивную мутацию через with выражения. Базовый тип, класс или структуру, по-прежнему определяет, используют ли экземпляры семантику ссылок или значений. Модификатор record добавляет понятное для данных поведение поверх этих семантик. Используйте записи, когда основная роль типа хранит данные и два экземпляра с одинаковыми значениями следует считать равными.
Когда следует использовать записи
Используйте запись, когда выполняются все следующие условия:
- Основная роль типа — хранение данных.
- Два экземпляра с одинаковыми значениями должны быть равными.
- Требуется неизменяемость (особенно для
record classтипов). - Вы хотите удобочитаемую
ToString, не записывая ее вручную.
При выборе между record class и record struct:
- Используйте
record classкогда требуется наследование или когда тип достаточно велик, чтобы копирование при каждом назначении было дорогостоящим. - Используйте
record structдля небольших автономных значений, когда полезны семантика копирования и выделение на стеке.
Избегайте записей типов сущностей в Entity Framework Core, которые зависят от ссылочного равенства для отслеживания сущностей. Более широкое сравнение параметров типа см. в разделе "Выбор типа".
Объявить запись
Можно применить record к классу или структуре. Простейшая форма использует позиционные параметры , определяющие конструктор и свойства в одной строке:
public record Person(string FirstName, string LastName);
Тот же позиционный синтаксис работает для record struct типов:
public record struct Coordinate(double Latitude, double Longitude);
public readonly record struct Temperature(double Celsius)
{
public double Fahrenheit => Celsius * 9.0 / 5.0 + 32.0;
}
Написание record является сокращением для record class, который является ссылочным типом. Запись record struct создает тип значения. Компилятор создает свойства из позиционных параметров в обоих случаях, но значения по умолчанию отличаются:
-
record class: свойства имеют значениеinit-only (неизменяемые после построения). -
record struct: свойства по умолчанию доступны для записи и чтения. Добавьтеreadonly(readonly record struct), чтобы сделать их толькоinit.
Записи также можно записывать с помощью стандартного синтаксиса свойств, если вам нужен дополнительный элемент управления. Например, чтобы сделать свойство чтение и запись вместо init-only:
public record Product
{
public required string Name { get; init; }
public decimal Price { get; set; }
}
record class и record struct
Поскольку модификатор сохраняет семантику базового типа, record и record class ведут себя по-другому при назначении или сравнении ссылок. Назначение record class копирует ссылку. Обе переменные указывают на один и тот же объект. Назначение record struct копирует данные, поэтому изменения одной переменной не влияют на другую.
// Record class — assignment copies the reference
var p1 = new Person("Grace", "Hopper");
var p2 = p1; // p1 and p2 point to the same object:
Console.WriteLine(ReferenceEquals(p1, p2)); // True
// Record struct — assignment copies the data
var c1 = new Coordinate(47.6062, -122.3321);
var c2 = c1;
c2.Longitude = 0.0; // mutating c2 doesn't affect c1
Console.WriteLine(c1.Longitude); // -122.3321
Console.WriteLine(c2.Longitude); // 0
Структуры записей также обеспечивают равенство значений, созданных компилятором:
var home = new Coordinate(47.6062, -122.3321);
var copy = home;
Console.WriteLine(home); // Coordinate { Latitude = 47.6062, Longitude = -122.3321 }
Console.WriteLine(home == copy); // True — value equality
Выберите record class , когда требуется наследование или когда экземпляры достаточно большие, чтобы копирование было дорогостоящим. Выберите record struct для небольших автономных данных, где семантика копирования, характерная для типов-значений, подходит. Дополнительные сведения о семантике типа значений см. в разделе "Структуры".
Равенство значений
Модификатор record предоставляет как классы, так и структуры, созданные компилятором, равенство свойств по свойству. Вот как равенство работает во всех четырех типах:
-
Обычный класс: использует равенство ссылок по умолчанию. Оператор
==проверяет, указывают ли две переменные на один и тот же объект, а не совпадают ли данные. -
Простая структура: поддерживает равенство ValueType.Equalsзначений, но реализация по умолчанию использует отражение, которое медленнее и не создает
==/!=операторы. -
record class: компилятор создает методыEqualsиGetHashCode, а также операторы==/!=, которые сравнивают каждое значение свойства. Два разных объекта с одинаковыми данными равны. -
record struct: То же равенство, сгенерированное компилятором дляrecord class, но без использования отражения, что делает его быстрее, чем обычное равенство структур.
В следующем примере показано равенство классов записей:
// Person is a record type with three properties: FirstName, LastName, and PhoneNumbers.
var phones = new string[] { "555-1234" };
var person1 = new Person("Grace", "Hopper", phones);
var person2 = new Person("Grace", "Hopper", phones);
Console.WriteLine(person1 == person2); // True
Console.WriteLine(ReferenceEquals(person1, person2)); // False
person1.PhoneNumbers[0] = "555-9999";
Console.WriteLine(person2.PhoneNumbers[0]); // 555-9999 — same array
Два Person экземпляра являются разными объектами, но они равны, так как все их значения свойств соответствуют. Свойства массива сравниваются по ссылке, а не по содержимому. Изменение общего массива отображается с помощью обеих записей, так как сам массив не копируется.
Недеструктивная мутация с with выражениями
Записи часто неизменяемы, поэтому вы не можете изменить свойство после создания.
with Выражение создает копию с одним или несколькими свойствами, оставляя исходную запись без изменений. Этот подход работает для обоих типов: record class и record struct
var original = new Person("Grace", "Hopper");
var modified = original with { FirstName = "Margaret" };
Console.WriteLine(original); // Person { FirstName = Grace, LastName = Hopper }
Console.WriteLine(modified); // Person { FirstName = Margaret, LastName = Hopper }
Console.WriteLine(original == modified); // False
var copy = original with { };
Console.WriteLine(original == copy); // True
Тот же синтаксис работает для record struct типов:
var shifted = home with { Longitude = -122.0 };
Console.WriteLine(shifted); // Coordinate { Latitude = 47.6062, Longitude = -122 }
Console.WriteLine(home == shifted); // False
Выражение with копирует существующий экземпляр, а затем применяет указанные изменения свойств.
Позиционные записи и деконструкция
Позиционные записи генерируют метод Deconstruct, который вы используете для извлечения значений свойств в отдельные переменные:
var (first, last) = person;
Console.WriteLine($"{first} {last}");
// Grace Hopper
Деконструкция работает с обоими record class и record struct типами. Его можно использовать в сопоставлениях назначений, foreach циклов и шаблонов.
Наследование записей
Один record class может наследоваться от другого record class. Запись не может наследоваться от обычного класса, и класс не может наследовать от записи:
public record Student(string FirstName, string LastName, int GradeLevel)
: Person(FirstName, LastName);
Проверки равенства значений включают тип времени выполнения, поэтому Person и Student с одинаковыми FirstName и LastName не считаются равными. Структуры записей не поддерживают наследование, так как структуры не могут наследоваться от других типов.