Какой самый базовый предок (корневой тип) всех объектов в .NET?

Ответ

Корневым предком для всех типов в .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 }

Ответ 18+ 🔞

Смотри, вот тебе базовая фишка .NET, без которой нихуя не работает. Все типы — и классы, и структуры — в итоге выходят из одного корня: System.Object (или просто object в C#). Это как общий предок в генеалогическом древе, только для кода.

Зачем это нужно? Ну, чтобы был единый порядок. Любую хуйню можно запихнуть в переменную типа object. Если это структура (типа int), она запакуется (boxing), но впихнется — факт.

Вот что этот object всем своим детям даёт (виртуальные методы, которые можно переписать):

Метод Зачем нужен Важные нюансы
Equals(object) Проверяет, равны ли объекты по значению. Если переопределяешь его, обязательно переопредели и GetHashCode(), а то всё ебнется.
GetHashCode() Выдаёт хэш для хэш-таблиц (Dictionary, HashSet). Для одинаковых объектов должен возвращать одинаковый хэш.
ToString() Делает из объекта строку для отладки или вывода. По умолчанию выдаёт полное имя типа — обычно бесполезно. Переопределяй почти всегда.
GetType() Показывает реальный тип объекта в рантайме. Не виртуальный, его не переписать.
MemberwiseClone() Делает поверхностную копию (shallow copy). Защищённый метод, просто копирует поля.
ReferenceEquals() Статический метод, сравнивает именно ссылки.

Примерчик, чтобы въехать:

// Благодаря System.Object всё это легально
object obj1 = new MyClass(); // Класс — ок
object obj2 = 42; // int (структура) — упакуется в объект (boxing)
object obj3 = "Hello"; // string — тоже ок

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 } — уже читаемо!

Короче, object — это такой фундамент, на котором всё держится. Даже если ты про него не думаешь, он есть. Как гравитация, блядь.