Урок по треку C# и .NET 7 мин

ООП в C#

Классы и интерфейсы, virtual, override и new, абстрактные классы, модификаторы доступа.

На собесе по ООП редко спрашивают определения. Чаще дают код с virtual, new и конструкторами и просят предсказать вывод, а потом обсуждают дизайн: интерфейс или абстрактный класс, наследование или композиция.

Четыре принципа на языке C#

  • Инкапсуляция: состояние закрыто (private), меняется только методами, которые держат инварианты. Свойство public decimal Balance { get; private set; } нельзя уменьшить снаружи.
  • Наследование: class Admin : User, один базовый класс, сколько угодно интерфейсов.
  • Полиморфизм: virtual/override и интерфейсы, вызов идёт по реальному типу объекта.
  • Абстракция: abstract-классы и интерфейсы описывают контракт без деталей.

virtual, override, new

class Base { public virtual string Name() => "Base"; public string Hi() => "Base"; }
class Derived : Base
{
    public override string Name() => "Derived";
    public new string Hi() => "Derived";
}

Base b = new Derived();
b.Name(); // "Derived": виртуальный вызов
b.Hi();   // "Base": new скрывает метод, а не переопределяет

override участвует в полиморфизме, new нет: какой метод вызовется, решает статический тип переменной. Без new компилятор предупредит (CS0108). sealed override запрещает переопределять дальше.

Абстрактный класс или интерфейс

Абстрактный классИнтерфейс
Наследованиеодин базовый классмного интерфейсов
Поля и конструкторыестьнет
Реализация методовестьс C# 8 методы по умолчанию
Статикаестьс C# 11 static abstract

Интерфейс описывает возможность («умеет сохраняться»), абстрактный класс общую основу семейства с состоянием («шаблонный метод»).

Модификаторы доступа

public, private (по умолчанию для членов), protected, internal (внутри сборки, по умолчанию для классов), protected internal (наследники ИЛИ сборка), private protected (наследники И сборка), file (C# 11, только в файле).

Порядок инициализации

При new Derived() в C#:

  1. Статический конструктор Derived (один раз).
  2. Инициализаторы полей Derived.
  3. Статический конструктор Base (при первом обращении к типу).
  4. Инициализаторы полей Base.
  5. Тело конструктора Base.
  6. Тело конструктора Derived.

Отсюда ловушка: виртуальный метод, вызванный из конструктора базы, выполнится в наследнике, у которого тело конструктора ещё не отработало. Поля, заданные в теле, будут нулевыми.

Статика

Статическое поле одно на тип (для дженерика одно на каждый закрытый тип). Статический конструктор вызывается средой один раз, потокобезопасно, перед первым использованием. Исключение в нём делает тип непригодным: дальше TypeInitializationException.

SOLID коротко

  • S: одна причина для изменения класса.
  • O: расширяем новыми реализациями, а не правкой switch.
  • L: наследник подставим вместо базы без сюрпризов (NotImplementedException в реализации интерфейса это красный флаг).
  • I: узкие интерфейсы.
  • D: зависимость от абстракций, внедрение через конструктор.

Композиция вместо наследования

Наследование жёстко связывает с базовым классом. Чтобы добавить поведение, чаще пишут декоратор: CachedRepository : IRepository принимает внутренний репозиторий и добавляет кеширование. Наследование оставляют для настоящего «является» и шаблонного метода, как у BackgroundService.ExecuteAsync.

Частые ошибки

  • Путать new и override.
  • Вызывать виртуальные методы в конструкторе.
  • Делать protected поля вместо protected методов: наследники ломают инварианты.
  • Строить глубокие иерархии ради переиспользования кода.

Что спросят на собесе

  • Чем override отличается от new?
  • Абстрактный класс или интерфейс: что и когда?
  • В каком порядке выполняются конструкторы и инициализаторы полей?
  • Расскажите SOLID на примерах из своего кода.
  • Почему «предпочитайте композицию наследованию»?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.