C# 9 представил новую функцию, называемую Запись (record), которая обеспечивает неизменяемость и семантику сравнения на основе значений для классов. Записи упрощают создание типов данных и обеспечивают лучшую безопасность при работе с ними. Однако до C# 10 они были доступны только для классов, теперь же у нас есть record struct, позволяющие применить те же преимущества Записей и к структурам.

В этой статье мы рассмотрим что такое записи для структур, примеры их использования и обсудим, как они могут быть полезны в реальных проектах.

Рекорды в C#: краткий пересказ

Прежде чем углубляться в record struct, давайте быстро вспомним, что такое Записи и почему они были введены в C#.

Записи — это специальный тип классов, которые предоставляют неизменяемость и сравнение на основе значений. Они идеально подходят для моделирования типов данных, таких как дата, время или координаты. Record обеспечивает сокращенный синтаксис для определения подобных типов и автоматически генерирует методы, такие как Equals, GetHashCode и ToString.

Пример использования:

public record Date(int Year, int Month, int Day);

Date date1 = new Date(2023, 3, 31);
Date date2 = new Date(2023, 3, 31);

Console.WriteLine(date1 == date2); // Вывод: True

Также, немаловажным для кого-то будет и переопределение ToString() для вывода всего содержимого - этакий встроенный сериализатор.

public record Date(int Year, int Month, int Day);

Date date = new Date(2023, 3, 31);

Console.WriteLine(date.ToString()); // Вывод: Date { Year = 2023, Month = 3, Day = 31 }

Record structs

Записи структур предоставляют все преимущества записей, такие как неизменяемость и сравнение на основе значений, для структур. Подобные переменные ведут себя так же, как обычные структуры, только с дополнительными возможностями, предоставляемыми записями. Можно сказать, что это такой синтаксический сахар, чтобы быстро добавить сравнение по значениям и иммутабельность. Однако, если быть до конца честными, то полный список преимуществ состоит не из двух, а аж из пяти пунктов.

1. Производительность и оптимизация памяти

Так как record struct является значимым типом, она хранится на стеке, что обеспечивает лучшую производительность и оптимизацию использования памяти по сравнению с record class. Записи структур идеально подходят для определения маленьких и часто используемых типов данных.

public record struct Vector2D(float X, float Y);
public record struct Vector3D(float X, float Y, float Z);
public record struct Color(byte R, byte G, byte B);

2. Сравнение на основе значений

Как и записи для классов, записи для структур предоставляют сравнение на основе значений, что позволяет сравнивать два экземпляра рекордной структуры на равенство, основываясь на их значениях, а не на их ссылках. Это делает их идеальным выбором для определения типов данных, для которых важно сравнение на основе значений, таких как числовые координаты или дата и время.

Point point1 = new Point(10, 20);
Point point2 = new Point(10, 20);

Console.WriteLine(point1 == point2); // Вывод: True

3. Неизменяемость

Применимо к структурам, записи обеспечивают неизменяемость, что означает, что после создания экземпляра его свойства не могут быть изменены. Это предотвращает случайные изменения состояния и обеспечивает большую надежность и безопасность при работе с данными. Неизменяемость также упрощает понимание и отладку кода, так как вы всегда можете быть уверены, что состояние объекта останется неизменным после создания.

Point point = new Point(10, 20);

// Строка №5 вызовет ошибку компиляции, так
// как свойства записи структуры неизменяемы
//point.X = 30;

4. Сокращенный синтаксис

Сокращенный синтаксис применяется для определения свойств и делает ваш код короче и читаемее. Он также уменьшает вероятность ошибок, связанных с опечатками или упущенными элементами. В какой-то степени форма записи похожа на сигнатуру конструктора:

public record struct Rectangle(int Width, int Height);

5. Сравнение в switch и деконструкция

Записи для структур также поддерживают возможность сравнения в switch-выражениях и деконструкцию, что позволяет вам легко извлекать значения свойств или использовать сопоставление с образцом для различных сценариев.

Rectangle rect = new Rectangle(100, 200);

// Деконструкция
(int width, int height) = rect;
Console.WriteLine($"Width: {width}, Height: {height}"); // Вывод: Width: 100, Height: 200

// Сопоставление с образцом
string description = rect switch
{
    (0, 0) => "Пустой прямоугольник",
    (var w, var h) when w == h => "Квадрат",
    (var w, var h) => $"Прямоугольник: ширина {w}, высота {h}"
};

Заключение

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


Можно ли работать и не знать паттернов - да. Можно ли с ними не столкнуться - нет. Приглашаю вас на бесплатный урок, где обсудим паттерны проектирования в C#: абстрактная фабрика, декоратор и другие.

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


  1. DistortNeo
    03.04.2023 09:49
    +2

    Записи структур предоставляют все преимущества записей, такие как неизменяемость

    Это неверно. Чтобы record struct стала неизменяемой, надо объявить её как readonly record struct.


    1. onyxmaster
      03.04.2023 09:49
      +1

      А чего вы минусуете человека? В документации так и написано.


      1. DistortNeo
        03.04.2023 09:49
        +5

        Это просто показатель, что язык C# идёт по стопам C++. Язык становится насколько сложным, что никто уже не знает его на 100%.


        1. onyxmaster
          03.04.2023 09:49

          В целом да, но скорость, к счастью, не та =)


      1. Vanirn
        03.04.2023 09:49
        +1

        Кому лень искать:

        Positional properties are immutable in a record class and a readonly record struct. They're mutable in a record struct.

        Как по мне, дак получилась каша из ключевых слов иммутабельности. Для супер сеньоров такие конструкции вполне понятны, а вот новичков в языке могут сильно запутать.


    1. odisseylm
      03.04.2023 09:49

      Ну что же, и я придерусь))

      Фраза "Записи структур предоставляют все преимущества записей, такие как неизменяемость", абсолютно корректна, поскольку компилятор/среда это предоставляет из коробки (девелоперу не нужно это вручную реализовывать). Просто упущенно автором, что для этого нужно добавить одно ключевое слово.

      Ваш комментарий полезен, но сильно категоричен))


      1. DistortNeo
        03.04.2023 09:49

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