Ответ
Краткий ответ: Зависит от способа сравнения. При использовании переопределенного метода Equals() или оператора == для структуры боксинга не происходит. Боксинг может случиться при приведении структуры к типу интерфейса или при вызове унаследованного от object метода.
Детали и примеры:
-
Сравнение БЕЗ боксинга (рекомендуемый способ):
public readonly struct Point : IEquatable<Point> { public int X { get; } public int Y { get; } // Реализация IEquatable<T> избегает боксинга public bool Equals(Point other) => X == other.X && Y == other.Y; // Переопределение Equals(object) требует боксинга для 'obj', но это редко используемый путь public override bool Equals(object obj) => obj is Point other && Equals(other); // Перегрузка оператора == public static bool operator ==(Point left, Point right) => left.Equals(right); } Point p1 = new Point(1, 2); Point p2 = new Point(1, 2); bool isEqual1 = p1.Equals(p2); // НЕТ боксинга (вызов IEquatable<Point>.Equals) bool isEqual2 = p1 == p2; // НЕТ боксинга (вызов перегруженного оператора) -
Сравнение, которое ВЫЗЫВАЕТ боксинг:
Point p1 = new Point(1, 2); Point p2 = new Point(1, 2); // БОКСИНГ: Приведение к необобщенному интерфейсу IEquatable ie = p1; // Боксинг для p1! bool isEqual3 = p1.Equals((object)p2); // Боксинг для p2 (вызов Equals(object)) // БОКСИНГ: Использование в коллекциях типа object (например, ArrayList) ArrayList list = new ArrayList(); list.Add(p1); // Боксинг при добавлении!
Почему это важно? Боксинг создает новый объект в куче (heap), что приводит к дополнительным затратам на выделение памяти и последующую сборку мусора (GC). В высокопроизводительном коде или в циклах это может стать узким местом. Всегда реализуйте для структур интерфейс IEquatable<T> и используйте обобщенные коллекции (List<Point> вместо ArrayList).
Ответ 18+ 🔞
Давай разберёмся с этим вопросом про боксинг структур при сравнении, а то народ часто путается, как сука слепая в луже.
Вот смотри, если ты написал свою структуру и не поленился сделать всё по уму, то боксинг при сравнении не происходит. Но это только если ты не еблан и не используешь кривые пути.
Как правильно, чтобы не было боксинга:
Ты делаешь так — реализуешь IEquatable<T> для своей структуры. Это главная фишка. Вот смотри на пример, он как шпаргалка для неёбистов:
public readonly struct Point : IEquatable<Point>
{
public int X { get; }
public int Y { get; }
// Вот это — наш козырь. Этот метод жрёт такую же структуру, без всякого боксинга.
public bool Equals(Point other) => X == other.X && Y == other.Y;
// А этот метод — legacy-говно, его вызывают старые коллекции. Тут бокс для `obj` будет, но если ты не дебил, ты редко сюда попадёшь.
public override bool Equals(object obj) => obj is Point other && Equals(other);
// И оператор сравнения перегрузим, почему бы и нет? Тоже без бокса.
public static bool operator ==(Point left, Point right) => left.Equals(right);
}
// Используем
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
bool isEqual1 = p1.Equals(p2); // Вызовется bool Equals(Point other) — НИКАКОГО БОКСА! Всё чисто.
bool isEqual2 = p1 == p2; // Вызовется наш оператор — опять всё на стеке, красота.
Вот так вот. Если структура реализует IEquatable<T>, то вызов Equals с таким же типом — это прямые погоны, никакой упаковки в объект.
А теперь, как вызвать боксинг и просрать производительность:
Надо быть конченым оптимизатором, чтобы специально так делать, но знать стоит.
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
// Способ 1: Напрямую вызвать унаследованный от object метод.
bool isEqual3 = p1.Equals((object)p2); // А вот тут, блядь, ПРИВЕТ! p2 запакуется в объект (боксинг), потому что вызывается старый `Equals(object)`.
// Способ 2: Сунуть структуру в какой-нибудь древний пиздец типа ArrayList.
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(p1); // Тут при добавлении происходит пиздец — полный боксинг, потому что коллекция работает с object.
// И потом все сравнения через её методы будут с распаковкой-упаковкой, ад и срака.
// Способ 3: Привести к интерфейсу, который структура реализует.
IEquatable<Point> ie = p1; // И тут, внимание, тоже боксинг! Потому что интерфейс — это ссылочный тип.
Итог и мораль, которую надо высечь на жопе:
Боксинг — это когда твоя легковесная структура на стеке тупо заворачивается в объект в куче, со всеми вытекающими: лишняя аллокация, нагрузка на GC. В циклах на миллион итераций — это пиздец, а не оптимизация.
Чтобы этого не было:
- Всегда реализуй
IEquatable<T>для своих значимых типов. - Используй нормальные, обобщённые коллекции (
List<Point>,Dictionary<Point, ...>), а не legacy-уёбища вродеArrayListилиHashtable. - Сравнивай через
==(если перегрузил) или черезEquals(Point), а не через приведение кobject.
Если будешь следовать этим простым правилам, твой код будет летать, а не ебашить аллокации на ровном месте. Всё, вопрос закрыт.