Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

4 года назад
Открыть в
День 1399. #ЗаметкиНаПолях #TipsAndTricks Современные Приёмы в C#. Часть 2 Часть 1 Записи-Значения Записи-значения представляют собой форму более общего паттерна «объект-значение», специфичную для последних версий C#. Антипаттерн, с которым мы здесь боремся, называется Одержимость Примитивами (Primitive Obsession). Разработчик чрезмерно использует примитивы (string, int, Guid, decimal и т.д.) для представления концепций бизнеса или предметной области. Одним из классических примеров являются идентификаторы сущностей. Здесь есть 2 проблемы: 1. Примитивы не являются типобезопасными. Легко случайно передать идентификатор клиента методу, ожидающему идентификатор ресурса. Или, если методу нужны и customerId, и resourceId, можно легко передать параметры в неправильном порядке. 2. Примитивы поддерживают операции, которые не имеют смысла. Например, если customerId имеет тип int, компилятор с радостью позволит вам разделить его на 2, но это не имеет смысла с точки зрения бизнеса. Решение – объекты-значения. Идея состоит в том, что вы определяете (простой) объект предметной области, который заключает примитивный тип в типобезопасную оболочку. Иногда оболочка может иметь какую-то логику (например, валидацию), но чаще всего это анемичные модели, которые занимают промежуточное положение между примитивами и полноценными объектами предметной области. Записи-значения Современный способ создать объект-значение в C# - это записи-значения:
public readonly record struct CustomerId(string Value);

По порядку: - Записи (record) обеспечивают семантику значений, дополненную равенством по значению и поддержкой ToString. - Структуры-записи (record struct) предоставляют оболочку типа значения, избегая выделения в куче (размер оболочки равен размеру обёрнутого значения). - Структуры-записи только для чтения обеспечивают неизменность. Поскольку определение типа представляет собой всего одну строку, все типы записей-значений можно собрать в одном файле, вроде Primitives.cs. Советы 1. Избегайте публичности Записи-значения лучше всего работают как внутренние типы. Не в смысле internal, а в смысле в ядре вашего приложения. Не пытайтесь настроить сериализацию записей-значений. Путь ваш код просто оборачивает примитивные типы, полученные извне:
CustomerId customerId = new(customerIdIntValue);
и наоборот, разворачивает их обратно при отправке информации:
int customerIdIntValue = customerId.Value;

2. Выбор примитивов для замены Не все примитивы должны быть записями-значениями. Рекомендуется использовать примитивы по умолчанию, а записи-значения в следующих ситуациях: - Есть похожие типы значений (концептуально похожие или просто имеющие похожее имя), которые используются вместе. Например, идентификаторы сущностей, имеющие похожие имена, и когда есть несколько методов, которые одновременно работают с разными типами идентификаторов. - Нужно гарантировать, что критически важный тип-значения должен использоваться только определённым образом. Например, компонент, отправляющий e-mail с персональной информацией должен использовать только подтверждённые адреса. Тогда он может принимать тип ValidatedEmail. А свойство этого типа задаётся у пользователя только при подтверждении e-mail. Неправильное использование В основном это чрезмерное использование записей-значений. Существует компромисс в поддержке: записи-значения увеличивают сложность кода. Каждый разработчик знает, что такое строка, но тип ValidatedEmail не понятен с первого взгляда. Примитивы — прекрасное решение для большинства данных. Итого Избирательная замена некоторых примитивных типов записями-значениями повысит безопасность типов, правильность и удобство сопровождения вашего кода. Источник: https://blog.stephencleary.com/2022/10/modern-csharp-techniques-2-value-records.html