Как реализовано переопределение методов в C#?

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

Ответ

Переопределение (override) в C# — это механизм, позволяющий производному классу предоставить свою собственную реализацию виртуального или абстрактного метода, объявленного в базовом классе. Реализация основана на таблице виртуальных методов (Virtual Method Table, VMT).

Синтаксис и правила:

public class LoggerBase
{
    // 1. Метод должен быть помечен как virtual, abstract или уже override
    public virtual void Log(string message)
    {
        Console.WriteLine($"[BASE] {DateTime.Now}: {message}");
    }
}

public class FileLogger : LoggerBase
{
    // 2. Сигнатура метода (имя, тип возвращаемого значения, параметры) должна полностью совпадать
    // 3. Модификатор доступа должен быть таким же или менее строгим
    public override void Log(string message)
    {
        // 4. Можно обратиться к реализации базового класса через base
        base.Log(message); // Вызовет LoggerBase.Log
        // Новая логика для записи в файл
        File.AppendAllText("log.txt", message + "n");
    }
}

Что происходит под капотом?

  1. Класс LoggerBase имеет в своей VMT запись, указывающую на реализацию LoggerBase.Log.
  2. Класс FileLogger создаёт свою VMT. Для метода Log запись указывает на новую реализацию (FileLogger.Log).
  3. При вызове logger.Log(...) где logger имеет тип LoggerBase, но ссылается на объект FileLogger, CLR смотрит в VMT реального типа объекта (FileLogger) и вызывает соответствующую реализацию. Это и есть полиморфизм.

Отличия от сокрытия (new):

public class A { public void Method() => Console.WriteLine("A"); }
public class B : A { public new void Method() => Console.WriteLine("B"); } // Сокрытие

A obj = new B();
obj.Method(); // Выведет "A"! Выбор метода определяется типом переменной (A), а не объекта.
((B)obj).Method(); // Выведет "B"

Ключевые моменты:

  • sealed override — запрещает дальнейшее переопределение метода в цепочке наследования.
  • Переопределять можно только виртуальные члены. Попытка переопределить невиртуальный метод приведёт к ошибке компиляции.
  • Свойства и индексаторы также могут быть виртуальными и переопределяемыми.