← Все статьи

C# Record Types в 2026: Замена DTO и не только

Если вы пишете на C# последние несколько лет, вы наверняка слышали о record-типах. Они были представлены в C# 9.0 как легковесные иммутабельные структуры данных и быстро стали модным трендом. Но к 2026 году они перестали быть просто новинкой — это полноценный инструмент, который перекраивает архитектурные подходы в.NET-разработке. Многие до сих пор используют их лишь как удобную замену для DTO (Data Transfer Object), упуская из виду их настоящую мощь. Давайте разберемся, как использовать records не просто для синтаксического сахара, а для фундаментального повышения надежности и читаемости вашего кода.

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

Синтаксис records делает объявление таких моделей невероятно лаконичным. Сравните классический класс DTO и его record-аналог.

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

Теперь посмотрите на record: public record OrderSubmittedEvent(int OrderId, string CustomerEmail, DateTime SubmittedAt);

Одной строкой вы объявляете тип со всеми этими свойствами, полностью готовый к использованию. Компилятор автоматически генерирует для вас методы Equals, GetHashCode, ToString(), а также метод деконструкции (Deconstruct). Но главное — он генерирует метод `with` выражения.

Выражения `with` — это ключ к работе с иммутабельностью без боли. Допустим, у вас есть объект конфигурации Config с десятком полей. Вам нужно создать его копию, изменив всего одно поле. var newConfig = config with { LogLevel = LogLevel.Debug }; Компилятор создаст точную копию объекта config и подставит новое значение для LogLevel. Все остальные поля будут скопированы "по значению". Это безопасно, эффективно (для типов значений происходит реальное копирование) и невероятно читаемо.

Где же применять records помимо очевидных DTO? Вот несколько практических паттернов 2026 года.

Первый паттерн — Модели Команд и Событий (Commands & Events) в архитектурах CQRS/Event Sourcing. Такие объекты по своей природе являются фактами прошлого или намерениями — их менять бессмысленно и опасно. public record SubmitOrderCommand(Guid CartId, string PromoCode); public record OrderShippedEvent(Guid OrderId, DateTime ShippingDate);

Второй паттерн — Функциональные Опции (Functional Options) или Результаты (Results). Вместо сложных исключений или nullable-типов можно возвращать ясный результат операции. public record OperationResult(bool IsSuccess); public sealed record SuccessResult(T Value): OperationResult(true); public sealed record ErrorResult(string Message): OperationResult(false);

Третий паттерн — Неизменяемые Настройки (Immutable Settings). Загрузили конфигурацию из appsettings.json один раз при старте приложения — и больше о ней не беспокоитесь. public record ApiSettings(string BaseUrl, string ApiKey);

Четвертый паттерн — Ключи Словарей (Dictionary Keys). Поскольку records по умолчанию сравниваются по значению всех своих свойств (а не по ссылке), они идеально подходят на роль составных ключей. var cache = new Dictionary(); cache[(userId: 1)] = userData;

Важно понимать разницу между `record class` (используется по умолчанию) и `record struct`, который появился позже. Record class является ссылочным типом и поддерживает наследование. Record struct является типом значения (как обычный struct), что может дать преимущества в производительности для небольших часто создаваемых объектов за счет размещения в стеке.

  • Когда вам нужна полноценная инкапсуляция с приватными полями и сложной бизнес-логикой внутри методов.
  • Когда объекту необходимо изменять свое внутреннее состояние после создания (например Entity в Domain-Driven Design).
  • Когда требуется тонкий контроль над жизненным циклом объекта или реализация сложных интерфейсов.

Распространенная ошибка новичков — делать все модели records только ради краткости синтаксиса. Это приводит к антипаттерну "анаemic domain model", где вся логика вынесена наружу, а сами модели представляют собой просто наборы данных без поведения.

Таким образом, records в современном C# — это не просто замена DTO, а философия проектирования, которая делает акцент на безопасности данных, предсказуемости потока выполнения и выразительности кода. Используйте их осознанно там, где данные важнее поведения, где неизменяемость является преимуществом, а не ограничением. Это позволит вам писать более чистый, менее подверженный ошибкам и легко тестируемый код, что остается главным приоритетом в разработке программного обеспечения в 2026 году

💬 Комментарии (7)
👤
support.center24
24.03.2026 04:51
Нейтрально. Везде пишут про records, но в легаси-проектах с .NET Framework их всё равно не использовать. Статья для новичков в современном .NET.
👤
security.officer99
25.03.2026 03:21
Спасибо за материал! Как раз переписываю старый сервис и присматриваюсь к records для команд и событий в CQRS.
👤
david.anderson77
26.03.2026 05:35
Согласен, многие коллеги до сих пор не видят дальше замены простых классов. А как вы считаете, records полностью заменят классы в будущем?
👤
tech.guru
27.03.2026 02:33
Интересный взгляд на эволюцию фичи. Жаль, что в статье мало конкретных примеров кода для 2026 года — какие именно новые возможности ожидаются?
👤
tech.guru
31.03.2026 06:07
Автор, а есть ли подводные камни при сериализации сложных record-типов с коллекциями или наследованием? Хотелось бы практических советов.
👤
daniel.white789
31.03.2026 08:24
Полезно! Меня особенно заинтересовала мысль про перекраивание архитектурных подходов. Буду ждать продолжения про Domain-Driven Design.
👤
maxim.ivanov92
01.04.2026 16:18
Отличная статья! Я уже активно использую record для DTO, но хотелось бы узнать больше про их применение в паттернах типа Value Object.