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

Как произойдёт сравнение если в структуре будет ссылочный тип?

2.0 Middle🔥 141 комментариев
#C# и ООП#Управление памятью

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

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

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

Сравнение структур со ссылочными типами

Когда в структуре (struct) содержится ссылочный тип (например, класс, массив, строка), процесс сравнения становится неоднозначным и потенциально опасным, если не понимать механику работы C#.

Механизм сравнения по умолчанию

По умолчанию для структур не определены операторы == и !=. При попытке сравнения двух структур с помощью == получим ошибку компиляции. Однако доступны два варианта:

  1. Метод Equals() (унаследованный от System.ValueType)
  2. Рефлексивное сравнение полей

Давайте рассмотрим пример:

public class ReferenceType
{
    public int Value;
}

public struct MyStruct
{
    public ReferenceType RefField;
    public int ValueField;
}

// Использование
var struct1 = new MyStruct { RefField = new ReferenceType { Value = 10 }, ValueField = 5 };
var struct2 = new MyStruct { RefField = new ReferenceType { Value =-Same Value 10 }, ValueField = 5 };
var struct3 = struct1; // Поверхностное копирование

Как работает Equals() для структур со ссылками?

Реализация Equals() в System.ValueType использует рефлексию для сравнения всех полей структуры:

bool areEqual = struct1.Equals(struct2); // Вернёт FALSE!

Почему false? Потому что:

  • ValueField сравнивается по значению (5 == 5 → true)
  • RefField сравнивается по ссылке (разные объекты в куче → false)

Даже если объекты в RefField идентичны по содержанию, они разные по ссылке. Структура struct3, созданная копированием struct1, даст другой результат:

bool areEqualCopy = struct1.Equals(struct3); // Вернёт TRUE

Здесь true, потому что struct3.RefField содержит ту же самую ссылку, что и struct1.RefField.

Критические проблемы такого сравнения

  • Производительность: Использование рефлексии медленное
  • Семантическая неоднозначность: Что мы действительно хотим сравнить?
  • Поведение по умолчанию может не подходить: Часто нужно глубокое сравнение

Решение: Переопределение Equals() и GetHashCode()

Для корректного сравнения необходимо переопределить методы:

public struct MyStruct : IEquatable<MyStruct>
{
    public ReferenceType RefField;
    public int ValueField;
    
    public override bool Equals(object obj)
    {
        return obj is MyStruct other && Equals(other);
    }
    
    public bool Equals(MyStruct other)
    {
        // Глубокое сравнение ссылочного поля
        bool refsEqual = (RefField == null && other.RefField == null) ||
                        (RefField != null && other.RefField != null && 
                         RefField.Value == other.RefField.Value);
        
        return refsEqual && ValueField == other.ValueField;
    }
    
    public override int GetHashCode()
    {
        // Важно: GetHashCode() должен быть согласован с Equals()
        int refHash = RefField?.Value.GetHashCode() ?? 0;
        return HashCode.Combine(refHash, ValueField);
    }
    
    // Операторы для удобства (C# 7.0+)
    public static bool operator ==(MyStruct left, MyStruct right) => left.Equals(right);
    public static bool operator !=(MyStruct left, MyStruct right) => !(left == right);
}

Важные рекомендации для Unity-разработки

  1. Избегайте изменяемых структур со ссылками - это источник ошибок
  2. Для компонентов ECS используйте чистые value-типы
  3. Для данных, передаваемых между системами реализуйте IEquatable<T>
  4. Помните о boxing при использовании структур с интерфейсами
  5. В Unity особенно осторожно с Mesh, Material, Texture в структурах

Вывод

Сравнение структур со ссылочными типами по умолчанию работает некорректно для большинства практических случаев. Реализация по умолчанию сравнивает ссылки, а не содержимое объектов. Для осмысленного сравнения необходимо переопределять Equals() и GetHashCode(), реализуя глубокое сравнение или явно определяя семантику сравнения для вашего конкретного случая. В Unity-разработке предпочтительнее либо избегать таких гибридных типов, либо тщательно проектировать их поведение.