C#JuniorTechnical
В чём различия между value types и reference types в C#?
Value types (int, struct, enum) хранятся по значению и копируются при присваивании; reference types (class, string, array) хранят ссылку на объект в куче. Это влияет на семантику копирования, null-возможность и производительность.
Value Types и Reference Types
Каждый тип в C# относится к одной из двух категорий, определяющих, где хранятся данные и как они передаются между переменными.
Ключевые отличия
- Value types:
int,double,bool,char,struct,enum,decimal,DateTime. Хранят данные непосредственно. При присваивании создаётся полная копия. - Reference types:
class,string,object,interface, массивы, делегаты. Переменная хранит ссылку (адрес) на объект в куче. При присваивании копируется ссылка, объект остаётся один.
Семантика копирования
// Value type — независимые копии
int a = 5;
int b = a;
b = 10;
Console.WriteLine(a); // 5 — не изменился
// Reference type — общий объект
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1; // копируем ссылку!
list2.Add(4);
Console.WriteLine(list1.Count); // 4 — оба указывают на один объект
// Struct — копирование по значению
public struct Point { public int X; public int Y; }
var p1 = new Point { X = 1, Y = 2 };
var p2 = p1; // копия
p2.X = 99;
Console.WriteLine(p1.X); // 1 — оригинал не изменился
Передача в методы
// Value type передаётся по значению — метод получает копию
void IncrementValue(int n) => n++;
int x = 5;
IncrementValue(x);
Console.WriteLine(x); // 5 — не изменился
// ref — передаём по ссылке
void IncrementRef(ref int n) => n++;
IncrementRef(ref x);
Console.WriteLine(x); // 6
// Reference type — метод работает с тем же объектом
void AddItem(List<int> items) => items.Add(42);
var list = new List<int>();
AddItem(list);
Console.WriteLine(list.Count); // 1
// Но переприсвоение параметра не затрагивает оригинал
void Replace(List<int> items) => items = new List<int> { 0 };
Replace(list);
Console.WriteLine(list.Count); // 1 — оригинальная ссылка не изменилась
Null-возможность
// Value types не могут быть null (без Nullable)
int n = null; // ошибка компиляции
// Nullable value type
int? nullableN = null;
if (nullableN.HasValue)
Console.WriteLine(nullableN.Value);
// Короткий синтаксис с оператором ??
int result = nullableN ?? -1;
// Reference types могут быть null
string? s = null;
Console.WriteLine(s?.Length ?? 0); // null-conditional operator
Struct vs Class — когда что выбрать
// Struct — для маленьких иммутабельных значений (координаты, цвет, деньги)
public readonly struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency) => (Amount, Currency) = (amount, currency);
}
// Class — для объектов с идентичностью, поведением, иерархией наследования
public class BankAccount
{
public Guid Id { get; } = Guid.NewGuid();
public decimal Balance { get; private set; }
public void Deposit(decimal amount) => Balance += amount;
}
Подводные камни
- Изменяемые (mutable) struct — неожиданное поведение при копировании: изменение локальной копии не отражается на оригинале. Делайте struct иммутабельными через
readonly struct. - Boxing — помещение value type в переменную типа
objectили интерфейс создаёт объект на куче:object o = 42— аллокация. - Struct в коллекциях:
List<MyStruct>— каждый элемент хранится как копия; изменение через индекс (list[0].X = 1) запрещено, нужна промежуточная переменная. - Сравнение reference types через
==по умолчанию сравнивает ссылки, а не содержимое (исключение — string, переопределяет==). - Большие struct в hot path — копирование при каждой передаче дороже передачи ссылки; используйте
in-параметры для readonly-доступа без копирования. - Наследование: struct не поддерживает наследование от других struct/class (только от интерфейсов).
Common mistakes
- Путать value types, reference types и копирование значений с похожим механизмом из другой версии или платформы.
- Игнорировать runtime-границы C#: lifecycle, DI scope, SQL translation, UI thread или platform API.
- Не обсуждать null/empty/error cases и поведение под нагрузкой.
What the interviewer is testing
- Кандидат объясняет value types, reference types и копирование значений на конкретном примере, а не только определением.
- Указывает последствия для производительности, тестируемости и поддержки.
- Различает документированное поведение текущего стека и устаревшие практики.