В чем разница между value type и reference type в C#?
Что проверяют
Понимаете ли вы, что копируется при присваивании, и знаете ли про упаковку.
Как отвечать
Значимые типы (struct, enum, все числовые, bool, char, кортежи-значения) хранят само значение. При присваивании и передаче в метод копируется содержимое, и две переменные становятся независимыми.
Ссылочные типы (class, interface, delegate, record class, массивы, string) хранят ссылку на объект в куче. Копируется ссылка, а объект остаётся один на всех.
Следствия, которые надо назвать:
- равенство по умолчанию: у значимых сравниваются поля, у ссылочных ссылки. Поэтому два одинаковых по содержимому объекта класса не равны, пока не переопределён
Equals. Исключение этоrecord, где сравнение по значению генерируется; null: значимый тип не может быть пустым безNullable<T>, то естьint?;- где живут: «значимые на стеке» это упрощение. Поле структуры внутри класса лежит в куче вместе с владельцем, элементы массива структур лежат в куче подряд, захваченная лямбдой переменная переезжает в сгенерированный класс;
- упаковка: приведение значимого типа к
objectили к интерфейсу создаёт объект-обёртку в куче. Это выделение памяти и нагрузка на сборщик, и оно легко случается незаметно: добавление в необобщённую коллекцию, форматирование строки, сравнение через необобщённый интерфейс.
Когда объявлять struct: тип маленький (ориентир около 16 байт), логически представляет одно значение, неизменяем и живёт недолго. Большая изменяемая структура это худший вариант: копирование дорогое, а изменения теряются, потому что из свойства или коллекции возвращается копия.
Полезно назвать readonly struct, который запрещает мутации и позволяет компилятору не делать защитных копий, и ref struct (Span<T>), который обязан оставаться на стеке.
Где спотыкаются
Говорят «значимые на стеке, ссылочные в куче» без оговорок.
Делают изменяемые структуры и теряют изменения.
Не замечают упаковку в горячем цикле.
Ждут, что два экземпляра класса с одинаковыми полями окажутся равны.