Ответ
Наследование в .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:
- Единый корень: Все классы неявно наследуются от
System.Object. Это обеспечивает наличие общих методов (ToString(),GetHashCode(),Equals()). - Таблица виртуальных методов (VMT): Каждый тип имеет таблицу, содержащую адреса виртуальных методов. При переопределении (
override) в производном классе соответствующая запись в его VMT заменяется на адрес новой реализации. Это обеспечивает полиморфное поведение. - Вызов конструкторов: Конструкторы не наследуются. При создании объекта производного класса цепочка конструкторов вызывается в порядке от базового к производному.
public class BaseClass
{
public BaseClass() { Console.WriteLine("Base constructor"); }
}
public class DerivedClass : BaseClass
{
// Компилятор неявно вставляет вызов base()
public DerivedClass() { Console.WriteLine("Derived constructor"); }
}
// Вывод: Base constructor -> Derived constructor
- Модификаторы:
sealed— запрещает наследование от класса или дальнейшее переопределение метода.abstract— класс нельзя инстанцировать, он предназначен только для наследования.virtual/override— для полиморфного поведения.new— для сокрытия (hiding) члена базового класса (создаёт новый, независимый член, не участвующий в полиморфизме).
Наследование vs. Композиция: В практике .NET-разработки часто предпочтение отдаётся композиции (включению объектов других классов) над наследованием для повышения гибкости и уменьшения связности кода, следуя принципу "предпочитайте композицию наследованию".
Ответ 18+ 🔞
Да ты посмотри, какая красота! Наследование в .NET — это как взять тачку отца, но накрутить на неё нитро и спойлер. Всё родное остаётся, но ты уже едешь быстрее и с другим звуком.
Смотри, как это выглядит в коде, тут всё просто, как два пальца:
public class Vehicle // Это батя. Его тачка.
{
public string Manufacturer { get; set; }
public virtual void StartEngine() => Console.WriteLine("Двигатель завёлся. Трррр.");
}
public class Car : Vehicle // А это я. Унаследовал тачку.
{
public int DoorCount { get; set; }
public override void StartEngine() => Console.WriteLine("ВРУМ-ВРУМ! Опа, поехали!"); // Переделал под себя.
}
А теперь, блядь, самое интересное — как эта магия под капотом работает. CLR тут не хухры-мухры, всё по уму сделано.
-
Все мы дети System.Object. Это как все мы в итоге вышли из обезьяны. Каждый твой класс, даже если ты этого не видишь, уже имеет методы вроде
ToString()илиGetHashCode(). Это база, хуле. -
Таблица виртуальных методов (VMT) — сердце полиморфизма. Представь себе телефонную книжку для методов. У базового класса своя книжка: "StartEngine — звони сюда". Когда ты делаешь
override, производный класс берёт эту книжку и поверх старой записи пишет: "StartEngine — не, братан, теперь звони сюда, по новому адресу". Поэтому когда вызываешь метод у объекта через ссылку на базовый класс, CLR лезет в книжку реального типа объекта и находит правильный номер. Красота! -
Конструкторы — цепочка вызовов. Конструкторы, сука, не наследуются. Это как нельзя унаследовать право первой ночи, извини. При создании объекта всё идёт по цепочке снизу вверх:
public class BaseClass { public BaseClass() { Console.WriteLine("Конструктор бати."); } } public class DerivedClass : BaseClass { // Компилятор сам, тихонечко, добавит вызов base() public DerivedClass() { Console.WriteLine("Конструктор сына."); } } // Вывод: Конструктор бати. -> Конструктор сына.Сначала батя всё подготовит, потом уже ты своё накручиваешь. Иначе можно ебнуться.
-
Модификаторы — наши регуляторы. Тут целый арсенал:
sealed— поставил на класс, и всё, приехали. От него больше никто родиться не может. Как стерилизация. Для метода — дальше переопределять нельзя.abstract— класс-призрак. Создать его экземпляр нельзя, только наследоваться. Он как идея, чертёж.virtual/override— классика для полиморфизма. Сделал метод виртуальным — дал детям шанс его переписать.new— вот это, блядь, опасная штука. Сокрытие. Ты не переопределяешь метод родителя, ты создаёшь рядом совершенно новый, одноимённый метод. И если будешь вызывать его через ссылку на базовый класс — работать будет старый метод бати. Частая причина, блядь, "ну почему не вызывается?!" на code review. Используй с умом, а лучше — с перепугу.
И главное, запомни, как Отче наш: в нормальной жизни композицию часто предпочитают наследованию. Наследование — это жёсткая сцепка, "is-a" (автомобиль является транспортным средством). А композиция — "has-a" (автомобиль имеет двигатель). Гибче, проще менять, меньше проблем. Не лезь с наследованием, где можно просто взять и включить один объект в другой. Это, блядь, как брать в долг у друга, а не у банка с его суровыми процентами-обязательствами.