Как вызвать базовый метод из переопределённого виртуального метода в C#
Что проверяют
Синтаксис здесь на одну строку, поэтому интервьюеру интереснее, понимаете ли вы, как работает виртуальный вызов и когда base вызывать обязательно. Типичные продолжения: можно ли вызвать реализацию «деда», минуя родителя, и что будет, если привести объект к базовому типу.
Как отвечать
Внутри override-метода базовая реализация вызывается через base.Display(). Дальше важны детали:
- вызов невиртуальный: для
base.Method()компилятор генерирует инструкциюcall, а неcallvirt, поэтому выполняется реализация ближайшего предка, где метод определён или переопределён, без повторной диспетчеризации; - только на шаг вверх: конструкции
base.baseнет. Если нужна логика прадеда, её выносят в отдельныйprotectedметод; - порядок имеет значение: в паттерне
Dispose(bool disposing)сначала освобождают своё, аbase.Dispose(disposing)вызывают в конце; вIdentityDbContextбезbase.OnModelCreating(builder)в начале не настроятся ключи таблиц Identity; - абстрактный метод вызвать нельзя: у
abstractнет реализации,base.M()даст ошибку компиляции; - конструкторы: для них аналог это
: base(args)в объявлении, базовый конструктор отрабатывает до тела производного.
Стоит упомянуть альтернативу. Если базовая логика обязательна, надёжнее не полагаться на то, что наследник не забудет base, а применить шаблонный метод: публичный невиртуальный метод выполняет обязательную часть и вызывает protected virtual хук, который наследник переопределяет.
Где спотыкаются
Думают, что приведение ((BaseClass)derived).Display() вызовет базовую версию. При override вызов всё равно уйдёт в реализацию производного класса, снаружи базовую версию так не достать.
Путают override и new. С new метод скрывает базовый, и какой код выполнится, решает статический тип переменной, а не реальный тип объекта.
Забывают вызвать base там, где базовый класс держит важное состояние, например в Dispose или в методах жизненного цикла фреймворка, и получают утечки ресурсов или недонастроенный объект.