Ответ
record и struct в C# предназначены для моделирования данных, но имеют принципиальные различия в поведении и предназначении.
Record (обычно record class)
- Тип: Ссылочный тип (по умолчанию —
record class). Хранится в управляемой куче (heap). - Неизменяемость: Создан для неизменяемых (immutable) данных. Свойства инициализируются при создании и не могут меняться (при использовании позиционного синтаксиса или
init-сеттеров). - Сравнение по значению: Автоматически генерирует методы
Equals,GetHashCode()и операторы==/!=, которые сравнивают значения всех свойств, а не ссылки. - Другие фичи: Встроенная поддержка
ToString()с выводом свойств, деконструкции (Deconstruct) и копирования с модификацией (withexpressions).
public record Person(string Name, int Age); // Неизменяемый record
var person1 = new Person("Alice", 30);
var person2 = new Person("Alice", 30);
Console.WriteLine(person1 == person2); // True (сравнение по значению)
Console.WriteLine(person1); // Person { Name = Alice, Age = 30 }
var person3 = person1 with { Age = 31 }; // Неизменяемое копирование
Struct (значимый тип)
- Тип: Значимый тип (value type). Хранится в стеке (stack) или внутри родительского объекта. Копируется по значению при присваивании.
- Изменяемость: По умолчанию изменяем (mutable), если не объявлен как
readonly struct. - Сравнение по значению: Не генерирует логику сравнения автоматически. По умолчанию используется
ValueType.Equals, который работает через рефлексию и медленно. Для производительности нужно реализовыватьIEquatable<T>и переопределятьEquals/GetHashCodeвручную. - Ограничения: Не может иметь конструктора без параметров (до C# 10), не поддерживает наследование.
public struct Point
{
public int X;
public int Y;
// Нет автоматического сравнения по значению!
}
var point1 = new Point { X = 5, Y = 10 };
var point2 = point1; // Копирование значения
point2.X = 20; // Изменяется только копия
Ключевой выбор:
- Используйте
recordдля неизменяемых моделей данных, DTO, где критично сравнение по значению и нужна встроенная функциональность (ToString,with). - Используйте
structдля небольших (менее 16 байт), часто создаваемых и копируемых объектов, где важна производительность и размещение в стеке (например, координаты, ключи словарей). Для неизменяемых структур объявляйтеreadonly struct.
Ответ 18+ 🔞
А, ну это же классика, про которую все такие умные книжки пишут, а на деле всё просто, как три копейки. Смотри, есть у нас в C# две штуки для данных — record и struct. И они, блядь, как братья, но один — отличник, а второй — хулиган из соседнего подъезда.
Record (этот, record class)
- Что за зверь? Ссылочный тип, то есть живёт в куче, как все нормальные классы. Но не простой, а с понтами.
- Главная фишка — неизменяемость. Создал — и всё, пиздец. Не перепишешь. Это как договор кровью подписал. Свойства задаются раз и навсегда (ну, или через
init, но это уже детали). - Сравнивает по значению автоматом. То есть не важно, одна это ссылка в памяти или нет. Если у двух таких
record-ов все поля одинаковые — они равны. ИEquals, иGetHashCode, и операторы==за тебя уже написали, ленивая жопа. - Прочие ништяки: Красивый
ToString()сам выводит всё, что внутри, умеет в деконструкцию и, охуенно, в копирование с изменениями черезwith.
public record Person(string Name, int Age); // Создал — и сиди, не дергайся.
var person1 = new Person("Alice", 30);
var person2 = new Person("Alice", 30);
Console.WriteLine(person1 == person2); // True, потому что значения одинаковые, ёпта!
Console.WriteLine(person1); // Выведет всё красиво: Person { Name = Alice, Age = 30 }
var person3 = person1 with { Age = 31 }; // Волшебство! Новый рекорд, старый не тронут.
Struct (значимый тип)
- Что за зверь? Значимый тип. Живёт там, где родился — в стеке или внутри родителя. При присваивании тупо копируется целиком, как будто фотографию сделал.
- Изменяемость? Да запросто! По умолчанию — полный разгул демократии, меняй что хочешь. Хочешь порядок — объявляй
readonly struct. - Сравнение? Ха-ха, нет. Тут тебе никто ничего автоматически не сгенерирует. По умолчанию будет использоваться медленный как черепаха
ValueType.Equalsна рефлексии. Хочешь быстро — пишиIEquatable<T>и переопределяй всё сам, вручную, как в старые добрые времена. - Ограничения: Наследование — ни-ни. Конструктор без параметров до недавних пор тоже был под запретом.
public struct Point
{
public int X;
public int Y;
// Никакого волшебства. Сравнивай сам, если надо.
}
var point1 = new Point { X = 5, Y = 10 };
var point2 = point1; // Всё, скопировал значение, как будто ксерокснул.
point2.X = 20; // Меняешь копию. Оригинал (point1) похуй.
Так что когда что брать?
- Берёшь
record, когда тебе нужна неизменяемая модель, DTO, или когда заебало вручную реализовывать сравнение. Или просто хочешь выглядеть современно на код-ревью. - Берёшь
struct, когда делаешь маленькую (желательно, чтобы меньше 16 байт), простую хуйню, которую постоянно копируют, и тебе до зарезу нужна производительность. Или когда твой внутренний перфекционист орет, что объект логически — значение (как та же точка), и ему в куче не место. Для порядка делай егоreadonly.
Вот и вся философия, без этих ваших умных слов. Выбирай по ситуации и не выёбывайся.