Можно ли сравнить любые объекты в C#?

«Можно ли сравнить любые объекты в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, но механизм и результат сравнения зависят от типа объекта и реализации методов сравнения.

Основные способы сравнения:

  1. Операторы == и !=
  2. Метод object.Equals()
  3. Метод object.ReferenceEquals()

Поведение по типам:

1. Ссылочные типы (классы): По умолчанию == и Equals() (без переопределения) сравнивают ссылки, а не содержимое объектов.

class Person { public string Name; }

var person1 = new Person { Name = "Alice" };
var person2 = new Person { Name = "Alice" };
var person3 = person1;

Console.WriteLine(person1 == person2);     // False (разные ссылки)
Console.WriteLine(person1.Equals(person2)); // False (разные ссылки)
Console.WriteLine(person1 == person3);     // True (одинаковые ссылки)

Для сравнения по значению необходимо переопределить Equals() и GetHashCode(), а также операторы ==/!=.

2. Значимые типы (структуры): Для struct метод Equals() по умолчанию сравнивает значения всех полей (через рефлексию, что медленно). Оператор == для структур по умолчанию не определён.

public struct Point { public int X, Y; }

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

Console.WriteLine(p1.Equals(p2)); // True (значения полей равны)
// Console.WriteLine(p1 == p2);   // Ошибка компиляции: оператор == не определён

Рекомендации для кастомного сравнения:

  • Реализуйте интерфейс IEquatable<T> для типобезопасного и эффективного сравнения.
  • Всегда переопределяйте GetHashCode() согласованно с Equals().
  • Для ссылочных типов рассмотрите перегрузку операторов == и !=.
  • Используйте ReferenceEquals() для явной проверки ссылочной идентичности.
public class Product : IEquatable<Product>
{
    public int Id { get; set; }
    public string Name { get; set; }

    public bool Equals(Product other)
    {
        if (other is null) return false;
        return Id == other.Id && Name == other.Name;
    }

    public override bool Equals(object obj) => Equals(obj as Product);
    public override int GetHashCode() => HashCode.Combine(Id, Name);
}