Встречается на 34% собеседований по C#

Что такое полиморфизм в ООП

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

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

Полиморфизм в C# достигается тремя основными способами.

Переопределение методов (override)

Базовый класс объявляет метод как virtual, а производный класс переопределяет его с помощью override. Это позволяет вызывать метод через ссылку на базовый класс, но выполнять реализацию производного класса.

csharp
class Animal {
    public virtual void MakeSound() => Console.WriteLine("Some sound");
}

class Dog : Animal {
    public override void MakeSound() => Console.WriteLine("Woof!");
}

// Использование
Animal pet = new Dog();
pet.MakeSound(); // Выведет "Woof!"

Перегрузка методов (overload)

Перегрузка — это статический полиморфизм, когда несколько методов имеют одно имя, но различаются параметрами. Компилятор выбирает нужный метод на основе аргументов вызова.

csharp
void Print(int x) => Console.WriteLine(x);
void Print(string s) => Console.WriteLine(s);

Print(42); // Вызовет Print(int)
Print("hello"); // Вызовет Print(string)

text

### Интерфейсы

Интерфейс определяет контракт, а каждый класс, реализующий его, предоставляет собственную реализацию методов. Это позволяет обрабатывать разнотипные объекты единообразно.

```csharp
interface IDrawable {
    void Draw();
}

class Circle : IDrawable {
    public void Draw() => Console.WriteLine("Drawing circle");
}

class Square : IDrawable {
    public void Draw() => Console.WriteLine("Drawing square");
}

// Использование
List<IDrawable> shapes = new List<IDrawable> { new Circle(), new Square() };
foreach (var shape in shapes) {
    shape.Draw(); // Вызовет реализацию каждого класса
}

Пример

Рассмотрим практический пример: система обработки платежей. Базовый класс Payment определяет виртуальный метод Process, а производные классы переопределяют его.

csharp
class Payment {
    public virtual void Process() => Console.WriteLine("Processing payment");
}

class CreditCardPayment : Payment {
    public override void Process() => Console.WriteLine("Processing credit card payment");
}

class PayPalPayment : Payment {
    public override void Process() => Console.WriteLine("Processing PayPal payment");
}

// Клиентский код
void HandlePayment(Payment payment) {
    payment.Process(); // Полиморфный вызов
}

HandlePayment(new CreditCardPayment());
HandlePayment(new PayPalPayment());

Здесь метод HandlePayment принимает Payment, но вызывает реализацию конкретного подкласса. Это позволяет добавлять новые способы оплаты без изменения клиентского кода.

Подводные камни

  • Забыть virtual/override: если метод не объявлен как virtual, его нельзя переопределить. Если в производном классе используется new вместо override, полиморфизм не сработает — вызовется метод базового класса.
  • Путаница между перегрузкой и переопределением: перегрузка — это статическое связывание (выбор метода на этапе компиляции), а переопределение — динамическое (на этапе выполнения).
  • Чрезмерное использование интерфейсов: слишком много мелких интерфейсов усложняют код. Используйте интерфейсы, когда есть реальная потребность в общем контракте.
  • Нарушение LSP (принцип подстановки Лисков): производный класс должен корректно заменять базовый. Если переопределённый метод ведёт себя неожиданно, это может сломать логику.

Когда использовать

Полиморфизм применяется, когда нужно:

  • Обрабатывать коллекции разнотипных объектов единообразно (например, список фигур для отрисовки).
  • Расширять функциональность без изменения существующего кода (открыто/закрыто принцип).
  • Реализовывать стратегии, когда поведение выбирается на лету (например, стратегии сортировки).
  • Строить архитектуру на основе интерфейсов для тестируемости и гибкости.

Коротко

  • Полиморфизм — это разное поведение объектов с общим интерфейсом.
  • В C# реализуется через virtual/override, перегрузку методов и интерфейсы.
  • Переопределение — динамический полиморфизм, перегрузка — статический.
  • Полиморфизм упрощает расширение кода и работу с разнотипными объектами через общий контракт.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы