← Назад к вопросам

Что такое значимые и ссылочные типы в C#?

1.6 Junior🔥 181 комментариев
#C# и ООП

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

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Значимые и ссылочные типы в C#

В C# все типы данных делятся на две фундаментальные категории: значимые типы (value types) и ссылочные типы (reference types). Это разделение определяет поведение переменных при присваивании, передаче в методы и управлении памятью.

Основные различия

Место хранения в памяти:

  • Значимые типы хранятся в стеке (stack). Стек — это область памяти с быстрым доступом, организованная по принципу LIFO (Last In, First Out). Здесь же хранятся указатели на объекты в куче и информация о вызовах методов.
  • Ссылочные типы хранятся в управляемой куче (managed heap). Куча — это динамическая область памяти, время жизни объектов в которой управляется сборщиком мусора (Garbage Collector, GC). Переменная ссылочного типа в стеке содержит лишь адрес (ссылку) на место в куче, где находятся реальные данные.

Семантика присваивания и копирования:

  • При работе с значимыми типами копируется полное значение данных.
    int a = 10;
    int b = a; // Копируется значение 10. В памяти создаётся новая независимая копия.
    b = 20;    // Изменяется только b. Переменная a остаётся равна 10.
    Console.WriteLine(a); // Вывод: 10
    
  • При работе с ссылочными типами копируется только ссылка (адрес в памяти), а не сам объект. Обе переменные начинают указывать на один и тот же объект в куче.
    StringBuilder str1 = new StringBuilder("Hello");
    StringBuilder str2 = str1; // Копируется ссылка. str1 и str2 теперь указывают на один объект.
    str2.Append(" World!");    // Изменение объекта по ссылке str2 затрагивает и str1.
    Console.WriteLine(str1); // Вывод: "Hello World!"
    

Значение по умолчанию:

  • Для значимых типов — это default(T), обычно нулевое значение (0, false и т.д.).
  • Для ссылочных типов — всегда null, что означает отсутствие ссылки на объект.

Наследование:

  • Значимые типы являются запечатанными (sealed) и не могут участвовать в наследовании (хотя могут реализовывать интерфейсы).
  • Ссылочные типы могут наследовать от других классов и быть унаследованными.

Какие типы куда относятся?

Значимые типы:

  • Все примитивные типы: int, float, bool, char, double, long и т.д.
  • Пользовательские структуры (struct) и перечисления (enum).
  • Кортежи (Tuple) (как ValueTuple).
  • Nullable-типы (int?, Nullable<T>).

Ссылочные типы:

  • Классы (class).
  • Интерфейсы (interface) (переменная интерфейсного типа — ссылочная).
  • Массивы (int[], string[]).
  • Делегаты (delegate).
  • Строки (string) — несмотря на некоторую схожесть в поведении с значимыми типами (неизменяемость, частое переприсваивание), string является ссылочным типом.
  • Записи (record) (по умолчанию — ссылочные, но бывают и record struct).

Практические последствия и оптимизация

Выбор типа напрямую влияет на производительность:

  1. Значимые типы в стеке освобождаются мгновенно при выходе из контекста (метода). Это быстро и не нагружает GC. Однако частые операции упаковки (boxing) — преобразования значимого типа в ссылочный (object) — создают новый объект в куче и вредят производительности.
    int number = 42;
    object boxed = number; // Упаковка: значение копируется в кучу.
    int unboxed = (int)boxed; // Распаковка: значение копируется обратно в стек.
    
  2. Ссылочные типы в куче требуют выделения памяти и последующей уборки Garbage Collector'ом, что может вызывать кратковременные паузы в работе программы при больших объёмах данных. Однако передача крупных объектов по ссылке (без копирования) очень эффективна.

Правила выбора: Используйте struct для небольших, логически неделимых данных, которые часто создаются и уничтожаются (например, Point, Vector3, Color). Используйте class для объектов с сложной логикой, большим размером или требующих наследования.