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

Будет ли происходить Boxing если сравнивать две структуры?

2.3 Middle🔥 191 комментариев
#Основы C# и .NET

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

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

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

Отличный вопрос, который затрагивает тонкости работы с типами значений в .NET.

Краткий ответ

Нет, при сравнении двух структур (struct) с помощью стандартного оператора == (если он для этой структуры не перегружен) — boxing происходить не будет, но такое сравнение просто не скомпилируется. Однако, если структура реализует определенные интерфейсы или использует методы, требующие reference-тип, то boxing может произойти. Контекст сравнения — ключевой момент.

Давайте разберемся детально.

1. Стандартный оператор == для структур

Для пользовательских структур оператор == по умолчанию не определен. Попытка его использовать приведет к ошибке компиляции. Следовательно, ни о каком boxing'е речи не идет.

public struct Point
{
    public int X;
    public int Y;
}

Point p1 = new Point { X = 1, Y = 2 };
Point p2 = new Point { X = 1, Y = 2 };

// Ошибка компиляции CS0019:
// Оператор "==" не может применяться к операндам типа "Point" и "Point"
// bool areEqual = (p1 == p2);

2. Сравнение через перегруженный оператор ==

Если вы перегрузите оператор == в своей структуре, сравнение будет происходить без boxing'а, так как вызовется статический метод, принимающий параметры по значению.

public struct Point
{
    public int X;
    public int Y;

    public static bool operator ==(Point left, Point right)
    {
        // Сравнение полей. Boxing отсутствует.
        return left.X == right.X && left.Y == right.Y;
    }

    public static bool operator !=(Point left, Point right) => !(left == right);
}

Point p1 = new Point { X = 1, Y = 2 };
Point p2 = new Point { X = 1, Y = 2 };
bool areEqual = (p1 == p2); // OK. Boxing НЕ происходит.

3. Сравнение через метод Equals

Здесь ситуация более интересная и является основным источником путаницы.

  • Equals(object obj): Этот метод унаследован от System.Object и принимает object. При вызове на структуре происходит boxing структуры-аргумента (obj), а сама структура-получатель (на которой вызван метод) также упаковывается (boxing), если вызов не происходит через упакованную ссылку. Это крайне неэффективно.

    Point p1 = new Point { X = 1, Y = 2 };
    Point p2 = new Point { X = 1, Y = 2 };
    
    // ПЛОХО: Двойной boxing!
    // 1. p1 упаковывается для вызова виртуального метода через ссылку.
    // 2. p2 упаковывается для приведения к параметру типа object.
    bool areEqual = p1.Equals((object)p2);
    
  • IEquatable<T>.Equals(T other): Чтобы избежать boxing'а, структура должна реализовать интерфейс IEquatable<T>. Метод Equals в этом интерфейсе принимает параметр того же типа значения (T), поэтому упаковки не происходит.

    public struct Point : IEquatable<Point>
    {
        public int X;
        public int Y;
    
        public bool Equals(Point other) // Параметр типа Point (значение)
        {
            // Сравнение без boxing'а.
            return X == other.X && Y == other.Y;
        }
    
        // Также необходимо переопределить Equals(object) и GetHashCode() для согласованности.
        public override bool Equals(object obj) => (obj is Point other) && Equals(other);
        public override int GetHashCode() => HashCode.Combine(X, Y);
    }
    
    Point p1 = new Point { X = 1, Y = 2 };
    Point p2 = new Point { X = 1, Y = 2 };
    
    // ХОРОШО: Вызывается IEquatable<Point>.Equals(Point). Boxing НЕТ.
    bool areEqual = p1.Equals(p2);
    
  • Object.Equals(object, object): Статический метод Object.Equals для сравнения двух объектов. Обе структуры будут упакованы.

    bool areEqual = Object.Equals(p1, p2); // ПЛОХО: Двойной boxing.
    

4. Сравнение через интерфейсы (IComparable)

Если структура реализует необобщенный интерфейс (например, IComparable), и вы приводите её к этому интерфейсу, происходит boxing.

public struct Point : IComparable
{
    public int X;
    public int Y;
    public int CompareTo(object obj) { /*...*/ } // Принимает object
}

Point p1 = new Point { X = 1, Y = 2 };
Point p2 = new Point { X = 1, Y = 2 };

IComparable comparable = p1; // Boxing происходит здесь!
int result = comparable.CompareTo(p2); // И здесь для p2!

Используйте обобщенный интерфейс IComparable<Point>, чтобы избежать этой проблемы.

5. Сравнение с помощью ReferenceEquals

Метод Object.ReferenceEquals всегда принимает object. При передаче любых структур они будут упакованы, а результат всегда будет false, так как это разные экземпляры в boxing-куче.

bool isSameRef = Object.ReferenceEquals(p1, p2); // Бессмысленно. p1 и p2 упаковываются.

Итог и рекомендации

  1. Прямое сравнение == для структур без перегрузки — не компилируется.
  2. Boxing при сравнении происходит, когда структура используется как тип object или необобщенный интерфейс. Это характерно для унаследованного метода Equals(object), статического Object.Equals и необобщенных интерфейсов сравнения.
  3. Чтобы сравнение структур было эффективным (без boxing'а):
    *   **Перегрузите оператор `==`** (и `!=`).
    *   **Реализуйте интерфейс `IEquatable<T>`** и используйте его метод `Equals(T)`.
    *   Всегда переопределяйте `Equals(object)` и `GetHashCode()` для согласованности.
    *   Используйте обобщенные интерфейсы (`IEquatable<T>`, `IComparable<T>`) вместо необобщенных.

Таким образом, грамотно спроектированная структура (реализующая IEquatable<T>, с перегруженными операторами ==/!= и переопределенными методами от Object) позволяет производить сравнение полностью без операции boxing, что критически важно для производительности в высоконагруженных сценариях.

Будет ли происходить Boxing если сравнивать две структуры? | PrepBro