В чем разница между ссылочными и значимыми типами в C# и для чего они нужны
Что проверяют
Понимаете ли вы семантику копирования и знаете ли, что расположение в памяти определяется не типом, а контекстом.
Как отвечать
Значимые типы (int, double, bool, struct, enum) хранят само значение. Присваивание копирует содержимое, и копии независимы.
Ссылочные (class, string, массивы, делегаты) хранят ссылку. Присваивание копирует ссылку, оба имени указывают на один объект в куче.
Про память точнее, чем обычно говорят: локальная переменная значимого типа лежит на стеке, но поле значимого типа внутри класса и элемент массива структур лежат в куче вместе с владельцем. То есть «структура на стеке, класс в куче» это упрощение, верное лишь для локальных переменных.
Отсюда практические следствия:
- значимый тип передаётся в метод копией, и изменения внутри метода не видны снаружи, если не использовать
refилиout; - ссылочный передаётся ссылкой-копией: поля объекта изменить можно, а переприсвоить саму переменную вызывающего нет;
- значимые типы не бывают
null, для этого естьNullable<T>; - сравнение: у структур по значению, у классов по ссылке, если не переопределено.
Отдельно про упаковку: приведение значимого типа к object или интерфейсу создаёт объект в куче и копирует туда значение, распаковка копирует обратно. В цикле это заметная нагрузка на сборщик, из-за этого и появились обобщения и Span<T>.
Где спотыкаются
Говорят, что структуры всегда на стеке.
Считают string значимым типом, потому что он ведёт себя как значение: он ссылочный, просто неизменяемый.
Не знают про упаковку и её стоимость.
Путают передачу по ссылке с передачей ссылки по значению.