Что такое полиморфизм в ООП
Полиморфизм в ООП — это способность объектов с одинаковым интерфейсом вести себя по-разному в зависимости от их конкретного типа. В C# полиморфизм реализуется через переопределение виртуальных методов, перегрузку методов и реализацию интерфейсов. Это ключевой механизм, который позволяет писать гибкий и расширяемый код, работая с объектами через общий контракт, а не через конкретные типы.
Как это работает
Полиморфизм в C# достигается тремя основными способами.
Переопределение методов (override)
Базовый класс объявляет метод как virtual, а производный класс переопределяет его с помощью override. Это позволяет вызывать метод через ссылку на базовый класс, но выполнять реализацию производного класса.
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)
Перегрузка — это статический полиморфизм, когда несколько методов имеют одно имя, но различаются параметрами. Компилятор выбирает нужный метод на основе аргументов вызова.
void Print(int x) => Console.WriteLine(x);
void Print(string s) => Console.WriteLine(s);
Print(42); // Вызовет Print(int)
Print("hello"); // Вызовет Print(string)
### Интерфейсы
Интерфейс определяет контракт, а каждый класс, реализующий его, предоставляет собственную реализацию методов. Это позволяет обрабатывать разнотипные объекты единообразно.
```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, а производные классы переопределяют его.
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, перегрузку методов и интерфейсы. - Переопределение — динамический полиморфизм, перегрузка — статический.
- Полиморфизм упрощает расширение кода и работу с разнотипными объектами через общий контракт.