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

Как работает Equals с ссылочными типами?

2.0 Middle🔥 111 комментариев
#C# и ООП#Коллекции и структуры данных

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

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

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

Принцип работы Equals() для ссылочных типов

Работа метода Equals() со ссылочными типами в C# (и, соответственно, в Unity при использовании C#) основана на сравнении ссылок на объекты в управляемой куче, а не их внутреннего содержимого. Это поведение по умолчанию, унаследованное от класса System.Object.

Сравнение по ссылке (Reference Equality)

По умолчанию Equals() для ссылочного типа выполняет сравнение ссылок: метод возвращает true, только если обе переменные ссылаются на один и тот же экземпляр объекта в памяти.

// Пример в контексте Unity
GameObject obj1 = new GameObject("Object1");
GameObject obj2 = obj1; // Та же ссылка
GameObject obj3 = new GameObject("Object1"); // Новый объект

Debug.Log(obj1.Equals(obj2)); // True: одна и та же ссылка
Debug.Log(obj1.Equals(obj3)); // False: разные объекты в памяти
Debug.Log(obj1 == obj3);      // False: оператор == также сравнивает ссылки для классов

Переопределение Equals для семантического сравнения

Многие классы в .NET и Unity переопределяют метод Equals() для реализации сравнения по значению (value equality). Это означает, что метод сравнивает внутреннее состояние объектов.

// Пример со строкой (string) - ссылочный тип с переопределенным Equals
string str1 = "Hello";
string str2 = "Hello";
string str3 = new string('H', 'e', 'l', 'l', 'o'); // Новый объект в памяти

Debug.Log(str1.Equals(str2)); // True: содержимое одинаковое
Debug.Log(str1.Equals(str3)); // True: содержимое одинаковое, несмотря на разные ссылки
Debug.Log(ReferenceEquals(str1, str3)); // False: разные ссылки в памяти

Ключевые особенности в Unity

  1. Классы MonoBehaviour и ScriptableObject обычно не переопределяют Equals(), поэтому для них работает сравнение по ссылке.
  2. Структуры (value types) в C# по умолчанию сравнивают значения через Equals(), но они не являются ссылочными типами.
  3. Важность переопределения при необходимости сравнения содержимого:
public class CustomItem : MonoBehaviour
{
    public int Id;
    public string Name;
    
    // Переопределяем Equals для сравнения по Id и Name
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
            return false;
            
        CustomItem other = (CustomItem)obj;
        return Id == other.Id && Name == other.Name;
    }
    
    // При переопределении Equals обязательно переопределяем GetHashCode
    public override int GetHashCode()
    {
        return (Id.GetHashCode() * 397) ^ Name.GetHashCode();
    }
}

Рекомендации для Unity разработчиков

  • Для проверки одинаковых ссылок используйте ReferenceEquals() или оператор == (если он не перегружен).
  • Для сериализуемых данных (как в ScriptableObject) чаще требуется сравнение значений - реализуйте IEquatable<T>.
  • При переопределении Equals() всегда переопределяйте GetHashCode() для корректной работы с коллекциями (Dictionary, HashSet).
  • В Unity часто используют пользовательские сравнения для игровых объектов по идентификаторам или другим уникальным характеристикам, а не по ссылкам.

Понимание различия между сравнением ссылок и сравнением значений критически важно для корректной реализации игровой логики, особенно при работе с коллекциями объектов, системами сохранения и сетевым взаимодействием.