Ответ
Корневым предком для всех типов в .NET (как ссылочных class, так и значимых struct) является тип System.Object (синоним в C# — ключевое слово object).
Почему это важно? Это обеспечивает единую систему типов. Любой экземпляр можно присвоить переменной типа object (для значимых типов это потребует упаковки — boxing).
Базовые виртуальные методы, предоставляемые Object:
| Метод | Назначение | Особенности переопределения |
|---|---|---|
Equals(object) |
Определяет равенство объектов по значению. | При переопределении обязательно переопределите GetHashCode(). |
GetHashCode() |
Возвращает хэш-код для использования в хэш-таблицах (Dictionary, HashSet). |
Должен возвращать одинаковое значение для равных объектов. |
ToString() |
Возвращает строковое представление объекта. | По умолчанию выводит полное имя типа. Всегда полезно переопределять для отладки. |
GetType() |
Возвращает экземпляр Type, описывающий тип объекта во время выполнения (RTTI). |
Не виртуальный, его нельзя переопределить. |
MemberwiseClone() |
Создает поверхностную (shallow) копию объекта. | Защищенный метод. |
ReferenceEquals() |
Статический метод для сравнения ссылок. |
Пример на C#:
// Все эти присваивания корректны благодаря System.Object
object obj1 = new MyClass(); // Ссылочный тип
object obj2 = 42; // Значимый тип (int) -> происходит boxing
object obj3 = "Hello"; // Строка (ссылочный тип)
Console.WriteLine(obj2.GetType()); // Вывод: System.Int32
Console.WriteLine(obj1.Equals(obj3)); // Вывод: False (сравнение по умолчанию — по ссылке)
// Пример переопределения ToString() в своем классе
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString() => $"Product {{ Id = {Id}, Name = {Name} }}";
}
var product = new Product { Id = 1, Name = "Laptop" };
Console.WriteLine(product); // Вывод: Product { Id = 1, Name = Laptop }