Как реализовано наследование классов в .NET?

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

Ответ

Наследование в .NET реализуется на уровне CLR и поддерживает одиночное наследование реализации (один базовый класс) и множественное наследование интерфейсов.

Базовый синтаксис:

public class Vehicle // Базовый класс
{
    public string Manufacturer { get; set; }
    public virtual void StartEngine() => Console.WriteLine("Vehicle engine started.");
}

public class Car : Vehicle // Производный класс
{
    public int DoorCount { get; set; }
    public override void StartEngine() => Console.WriteLine("Car engine roars!");
}

Ключевые аспекты реализации в CLR:

  1. Единый корень: Все классы неявно наследуются от System.Object. Это обеспечивает наличие общих методов (ToString(), GetHashCode(), Equals()).
  2. Таблица виртуальных методов (VMT): Каждый тип имеет таблицу, содержащую адреса виртуальных методов. При переопределении (override) в производном классе соответствующая запись в его VMT заменяется на адрес новой реализации. Это обеспечивает полиморфное поведение.
  3. Вызов конструкторов: Конструкторы не наследуются. При создании объекта производного класса цепочка конструкторов вызывается в порядке от базового к производному.
public class BaseClass
{
    public BaseClass() { Console.WriteLine("Base constructor"); }
}

public class DerivedClass : BaseClass
{
    // Компилятор неявно вставляет вызов base()
    public DerivedClass() { Console.WriteLine("Derived constructor"); }
}
// Вывод: Base constructor -> Derived constructor
  1. Модификаторы:
    • sealed — запрещает наследование от класса или дальнейшее переопределение метода.
    • abstract — класс нельзя инстанцировать, он предназначен только для наследования.
    • virtual/override — для полиморфного поведения.
    • new — для сокрытия (hiding) члена базового класса (создаёт новый, независимый член, не участвующий в полиморфизме).

Наследование vs. Композиция: В практике .NET-разработки часто предпочтение отдаётся композиции (включению объектов других классов) над наследованием для повышения гибкости и уменьшения связности кода, следуя принципу "предпочитайте композицию наследованию".