Ответ
Record — это новый ссылочный тип (record class, по умолчанию) или тип-значение (record struct), представленный в C# 9.0 и улучшенный в последующих версиях. Его основное предназначение — моделирование неизменяемых (immutable) данных с семантикой равенства по значению (value-based equality).
Ключевые характеристики и "магия" компилятора:
- Неизменяемость по умолчанию (Positional Records): Свойства, объявленные в первичном конструкторе, автоматически становятся
init-only(инициализируются при создании и не могут быть изменены). - Равенство по значению: Для records автоматически генерируются методы
Equals(object?),GetHashCode()и операторы==/!=, которые сравнивают значения всех свойств, а не ссылки на объект. - Метод
ToString(): Автоматически генерирует удобное строковое представление, включающее имя типа и значения свойств. - Поддержка деконструкции (Deconstruction): Для positional records генерируется метод
Deconstruct, позволяющий легко разложить объект на переменные. - Выражение
with(Non-destructive mutation): Позволяет создать копию record с изменением одного или нескольких свойств, не изменяя оригинал.
Практические примеры:
// 1. Positional record (самый частый вариант)
public record Person(string FirstName, string LastName, int Age);
var person1 = new Person("John", "Doe", 30);
var person2 = new Person("John", "Doe", 30);
var person3 = person1 with { Age = 31 }; // Создание модифицированной копии
Console.WriteLine(person1 == person2); // True (равенство по значению)
Console.WriteLine(person1); // Person { FirstName = John, LastName = Doe, Age = 30 }
Console.WriteLine(person3); // Person { FirstName = John, LastName = Doe, Age = 31 }
var (fName, lName, age) = person1; // Деконструкция
// 2. Record с явно объявленными свойствами (если нужна валидация или логика)
public record Product
{
public Guid Id { get; init; } // init-only для неизменяемости
public string Name { get; init; }
public decimal Price { get; init; }
public Product(string name, decimal price)
{
if (price < 0) throw new ArgumentException("Price must be non-negative.");
Id = Guid.NewGuid();
Name = name;
Price = price;
}
}
// 3. Record struct (C# 10+)
public readonly record struct Point(int X, int Y); // readonly для полной неизменяемости
Идеальные сценарии использования:
- DTO (Data Transfer Objects) для API-запросов и ответов.
- Модели команд и событий в архитектурах CQRS/Event Sourcing.
- Ключи в словарях (благодаря корректно реализованным
EqualsиGetHashCode). - Любые объекты-значения, где важна неизменяемость и сравнение по содержанию.