← Назад к вопросам

Что такое Records в C# 9+? Чем они отличаются от обычных классов?

1.7 Middle🔥 131 комментариев
#ASP.NET и Web API#Тестирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Records в C# 9.0+

Records — это новый ссылочный тип, представленный в C# 9.0, предназначенный для создания неизменяемых (immutable) объектов данных, которые по умолчанию имеют семантику равенства по значению (value-like equality semantics). Основная философия записей — упростить моделирование данных, которые по своей природе являются неизменяемыми и идентифицируются своим содержимым.

Ключевые характеристики Records

1. Неизменяемость (Immutability)

Записи поощряют использование неизменяемых свойств через синтаксис positional records (позиционные записи), где свойства объявляются непосредственно в заголовке записи.

// Позиционная запись
public record Person(string FirstName, string LastName, int Age);

// Эквивалентный код компилятора создаст класс с init-only свойствами и конструктором

Этот синтаксис автоматически генерирует init-only свойства (с модификатором init вместо set), делая объект неизменяемым после создания.

2. Семантика равенства по значению

В отличие от обычных классов, где равенство по умолчанию основано на сравнении ссылок (ReferenceEquals), записи переопределяют методы Equals, GetHashCode и операторы ==/!= для сравнения всех значений свойств.

var person1 = new Person("Иван", "Иванов", 30);
var person2 = new Person("Иван", "Иванов", 30);

Console.WriteLine(person1 == person2); // True - сравниваются значения
Console.WriteLine(ReferenceEquals(person1, person2)); // False - разные объекты

3. Метод with для неразрушающего изменения (Non-destructive mutation)

Поскольку записи неизменяемы, для "изменения" создается копия с обновленными значениями свойств.

var original = new Person("Иван", "Иванов", 30);
var updated = original with { Age = 31 };

Console.WriteLine(original); // Person { FirstName = Иван, LastName = Иванов, Age = 30 }
Console.WriteLine(updated);  // Person { FirstName = Иван, LastName = Иванов, Age = 31 }

4. Автоматически генерируемые методы

Компилятор автоматически генерирует:

  • Конструктор для позиционных параметров
  • Свойства только для инициализации (init-only)
  • Переопределения ToString(), Equals(), GetHashCode()
  • Метод Deconstruct() для деконструкции
  • Метод Clone() для поддержки with-выражений
// Деконструкция
var (firstName, lastName, age) = person1;
Console.WriteLine(firstName); // Иван

Отличия от обычных классов

АспектRecordОбычный класс
ЦельМоделирование неизменяемых данныхУниверсальное представление объектов
РавенствоПо значению (сравниваются все данные)По ссылкам (по умолчанию)
НеизменяемостьПоощряется, свойства с initНе навязывается, свойства с set
ИзменениеЧерез with-выражения (новая копия)Прямое изменение состояния
НаследованиеПоддерживается, но с ограничениямиПолная поддержка
ИспользованиеDTO, Value Objects, сообщенияБизнес-логика, сервисы, контроллеры

Типы записей в C# 10+

C# 10 расширил возможности записей:

// Позиционная запись (C# 9)
public record Person(string Name, int Age);

// Структурная запись (C# 10) - value type
public record struct Point(int X, int Y);

// Классовая запись с явным объявлением (C# 10)
public record class Customer(string Id, string Name);

Пример практического использования

// DTO для API
public record ApiResponse<T>(bool Success, T Data, string Error = null);

// Value Object в Domain-Driven Design
public record Money(decimal Amount, string Currency)
{
    public static Money operator +(Money a, Money b)
    {
        if (a.Currency != b.Currency)
            throw new InvalidOperationException("Валюты должны совпадать");
        
        return a with { Amount = a.Amount + b.Amount };
    }
}

// Использование
var response = new ApiResponse<Person>(true, new Person("Анна", "Смирнова", 28));
var salary = new Money(1000, "USD") + new Money(500, "USD");

Когда использовать Records

  1. DTO (Data Transfer Objects) — передача данных между слоями
  2. Value Objects в DDD — объекты, идентифицируемые своими значениями
  3. Сообщения и события — особенно в event-sourcing и CQRS
  4. Ключи и составные идентификаторы — где важна семантика равенства
  5. Конфигурации и настройки — неизменяемые параметры

Ограничения и предостережения

  • Записи не заменяют обычные классы для объектов с поведением
  • Наследование записей требует осторожности при переопределении равенства
  • Для очень больших объектов (десятки свойств) копирование через with может быть накладно
  • Рефлексия и сериализация могут работать иначе из-за сгенерированных методов

Заключение

Records в C# — это мощное дополнение языка, которое заполняет пробел между структурами и классами, предоставляя тип, оптимизированный для работы с неизменяемыми данными. Они сокращают шаблонный код, повышают безопасность благодаря неизменяемости и делают намерения разработчика более явными. Однако важно понимать, что записи — не серебряная пуля, а специализированный инструмент, который следует использовать там, где их семантика соответствует требованиям домена.

Что такое Records в C# 9+? Чем они отличаются от обычных классов? | PrepBro