Ответ
Класс System.Object является корнем иерархии типов в C#. Все типы (как ссылочные, так и значимые через boxing) неявно наследуются от Object. Он предоставляет несколько виртуальных методов, которые можно и часто нужно переопределять в пользовательских классах для корректного поведения.
Виртуальные методы класса Object:
-
public virtual bool Equals(object? obj)- Назначение: Определяет семантическое равенство объектов (равенство значений), в отличие от равенства ссылок (
==по умолчанию для ссылочных типов). - Когда переопределять: Для классов, где два разных экземпляра могут считаться логически равными (например,
Money,DateTime,Personс одинаковымId). Всегда переопределяйтеGetHashCode()вместе сEquals(). -
Пример:
public class Product { public int Id { get; set; } public string Name { get; set; } public override bool Equals(object obj) { return obj is Product other && this.Id == other.Id; } // GetHashCode также должен быть переопределен (см. ниже) }
- Назначение: Определяет семантическое равенство объектов (равенство значений), в отличие от равенства ссылок (
-
public virtual int GetHashCode()- Назначение: Возвращает числовой хеш-код объекта, используемый в хеш-таблицах (
Dictionary<TKey, TValue>,HashSet<T>). - Критически важные контракты при переопределении:
- Если два объекта равны по
Equals(), они должны возвращать одинаковый хеш-код. - Хеш-код должен быть стабильным на протяжении жизненного цикла объекта (пока он находится в коллекции).
- Желательно, чтобы разные объекты возвращали разные хеш-коды для производительности.
- Если два объекта равны по
- Пример (сопряженный с
Equalsвыше):public override int GetHashCode() => Id.GetHashCode();
- Назначение: Возвращает числовой хеш-код объекта, используемый в хеш-таблицах (
-
public virtual string? ToString()- Назначение: Возвращает строковое представление объекта. Реализация по умолчанию возвращает полное имя типа.
- Когда переопределять: Для предоставления удобочитаемой отладочной информации. Широко используется при интерполяции строк (
$"{obj}") и вызовеConsole.WriteLine(obj). - Пример:
public override string ToString() => $"Product {{ Id = {Id}, Name = {Name} }}";
Невиртуальные (статические) методы:
public static bool ReferenceEquals(object? objA, object? objB)- Всегда проверяет равенство ссылок, даже для типов, перегрузивших оператор
==. Полезен, когда нужно гарантированно проверить, указывают ли две переменные на один экземпляр в памяти.
- Всегда проверяет равенство ссылок, даже для типов, перегрузивших оператор
public static bool Equals(object? objA, object? objB)- Статический помощник, который безопасно обрабатывает
nullи вызывает виртуальныйinstance.Equals().
- Статический помощник, который безопасно обрабатывает
Защищенный метод:
protected object MemberwiseClone()- Создает поверхностную копию (shallow copy) текущего объекта. Копируются значения полей значимых типов и ссылки для ссылочных типов (сами объекты не клонируются).
Пример использования ReferenceEquals и GetType:
var list1 = new List<int>() { 1, 2, 3 };
var list2 = list1; // Копируется ссылка
var list3 = new List<int>() { 1, 2, 3 }; // Новый объект с теми же данными
Console.WriteLine(Object.ReferenceEquals(list1, list2)); // True (одна ссылка)
Console.WriteLine(Object.ReferenceEquals(list1, list3)); // False (разные объекты)
Console.WriteLine(list1.Equals(list3)); // Для List<T> переопределен - может быть True (сравнение содержимого)
Console.WriteLine(list1.GetType().Name); // "List`1"
Console.WriteLine(list1.GetType().FullName); // "System.Collections.Generic.List`1[[System.Int32...]]"
Итог: Переопределение Equals, GetHashCode и ToString — стандартная практика для создания полноценных, предсказуемых и удобных в использовании типов данных в C#.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶