Что такое виртуальные методы в C# и как они работают?

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

Ответ

Виртуальные методы — это механизм поддержки полиморфизма в C#, позволяющий производному классу предоставить свою собственную реализацию метода, уже определенного в базовом классе. Выбор конкретной реализации происходит во время выполнения (позднее связывание) на основе фактического типа объекта.

Как это работает?

  1. В базовом классе метод помечается ключевым словом virtual.
  2. В производном классе метод переопределяется с помощью ключевого слова override.
  3. При вызове метода у ссылки типа базового класса, которая ссылается на объект производного класса, будет выполнена переопределенная версия.

Практический пример:

public class NotificationSender
{
    // Виртуальный метод с реализацией по умолчанию
    public virtual void Send(string message)
    {
        Console.WriteLine($"[Base Sender] Отправка базового уведомления: {message}");
    }

    // Невиртуальный метод. Его нельзя переопределить, можно только скрыть (new).
    public void Log(string action)
    {
        Console.WriteLine($"[Base Log] Действие: {action}");
    }
}

public class EmailSender : NotificationSender
{
    // Переопределение виртуального метода
    public override void Send(string message)
    {
        // Можно вызвать базовую реализацию, если нужно
        // base.Send(message);
        Console.WriteLine($"[Email] Отправлено на email: {message}");
    }
}

public class SmsSender : NotificationSender
{
    public override void Send(string message)
    {
        Console.WriteLine($"[SMS] Отправлено SMS: {message}");
    }

    // Скрытие (shadowing) метода базового класса, а не переопределение.
    public new void Log(string action)
    {
        Console.WriteLine($"[SMS Log] {action}");
    }
}

// Использование:
NotificationSender sender1 = new EmailSender();
NotificationSender sender2 = new SmsSender();
NotificationSender sender3 = new NotificationSender();

// Полиморфный вызов. Реализация определяется реальным типом объекта.
sender1.Send("Заказ создан"); // Вывод: [Email] Отправлено на email: Заказ создан
sender2.Send("Заказ создан"); // Вывод: [SMS] Отправлено SMS: Заказ создан
sender3.Send("Заказ создан"); // Вывод: [Base Sender] Отправка базового уведомления: Заказ создан

// Важно: для невиртуальных методов вызов определяется типом ссылки.
sender2.Log("test"); // Вывод: [Base Log] Действие: test (т.к. ссылка типа NotificationSender)
((SmsSender)sender2).Log("test"); // Вывод: [SMS Log] test
Ключевые отличия virtual/override от new: Аспект virtual / override new (скрытие)
Связывание Позднее (runtime) Раннее (compile-time)
Полиморфизм Поддерживает Не поддерживает
Вызов через ссылку базового класса Вызывается метод производного класса Вызывается метод базового класса

Зачем это нужно? Виртуальные методы — основа для реализации шаблонов проектирования (например, Template Method), создания расширяемых библиотек и фреймворков, где поведение базовых классов должно настраиваться наследниками.