Как работают виртуальные методы в C# и для чего они используются
Что проверяют
Понимаете ли позднее связывание: что вызов виртуального метода разрешается по фактическому типу объекта в рантайме, а не по типу переменной. И различаете ли virtual, abstract и new, потому что именно на этой границе вопрос и задают.
Как отвечать
virtual в базовом классе разрешает наследнику заменить реализацию через override. Компилятор выпускает callvirt, а конкретный адрес берётся из таблицы виртуальных методов того типа, который реально лежит в памяти. Отсюда и полиморфизм: переменная объявлена базовым типом, а работает реализация наследника.
Границы механизма:
virtualплюсoverrideэто позднее связывание;newэто сокрытие, метод выбирается по типу ссылки на этапе компиляции;abstractэто виртуальный без тела, наследник обязан его реализовать;sealed overrideзакрывает дальнейшее переопределение;- сигнатура и доступность при
overrideдолжны совпадать, базовую реализацию зовут черезbase.
class A { public virtual string Who() => "A"; }
class B : A { public override string Who() => "B"; }
class C : A { public new string Who() => "C"; }
A b = new B(), c = new C();
b.Who(); // B
c.Who(); // AГде спотыкаются
Путают new и override. Компилятор предупреждает про сокрытие, а кандидат уверяет, что разницы нет. Разница видна ровно тогда, когда объект наследника лежит в переменной базового типа, то есть в любом полиморфном коде.
Вызывают виртуальный метод из конструктора базового класса. Переопределённая версия выполнится раньше, чем конструктор наследника проинициализирует свои поля, и увидит там нули и null.
Рассуждают про дорогие виртуальные вызовы как про главный минус. JIT девиртуализует вызов, когда тип известен или класс запечатан, а реальная цена в том, что метод труднее заинлайнить. Это измеряют, а не угадывают, и на архитектурные решения такой аргумент почти никогда не влияет.