Ответ
Полиморфизм («много форм») — это принцип ООП, позволяющий использовать объекты разных классов через единый интерфейс (базовый класс или интерфейс), при этом конкретное поведение определяется реальным типом объекта во время выполнения.
Основные формы в C#:
-
Полиморфизм подтипов (наследование и переопределение): Базовый класс объявляет виртуальный метод, производные классы предоставляют свою реализацию.
public class Document { public virtual void Print() => Console.WriteLine("Печать базового документа."); } public class PdfDocument : Document { public override void Print() => Console.WriteLine("Отправка PDF на виртуальный принтер."); } public class WordDocument : Document { public override void Print() => Console.WriteLine("Запуск MS Word для печати."); } // Использование: один интерфейс, разное поведение List<Document> docs = new() { new Document(), new PdfDocument(), new WordDocument() }; foreach (var doc in docs) { doc.Print(); // Вызовется своя реализация для каждого типа } -
Полиморфизм через интерфейсы: Разные, не связанные наследованием классы, реализуют один интерфейс.
public interface ILogger { void Log(string message); } public class FileLogger : ILogger { public void Log(string message) => File.AppendAllText("log.txt", message); } public class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine($"[LOG] {message}"); } // Метод работает с любым ILogger public void ProcessOrder(ILogger logger) { logger.Log("Начало обработки заказа."); // ... логика } -
Перегрузка методов (Ad-hoc полиморфизм): Несколько методов с одним именем, но разными параметрами.
public class Calculator { public int Add(int a, int b) => a + b; public double Add(double a, double b) => a + b; public string Add(string a, string b) => a + b; // конкатенация }
Выгода: Полиморфизм уменьшает связанность кода. Вы можете добавлять новые типы (например, ExcelDocument), не изменяя существующий код, который работает с базовым типом Document.
Ответ 18+ 🔞
Смотри, вот эта штука — полиморфизм, или, по-нашему, «много форм». По сути, это когда ты можешь работать с кучей разных объектов через одну и ту же дверь, а внутри у них у каждого своя кухня. Как будто у тебя есть пульт от всего на свете с кнопкой «Включить», но один раз это телевизор, другой — кофемолка, а третий — вибратор тёти Зины. Нажимаешь одно и то же, а результат — разный, потому что внутри них пиздец какой разный код.
В C# это обычно выглядит тремя способами, и сейчас я тебе их разжую, как говно за щекой.
1. Полиморфизм через наследование (или «когда дети умнее родителей»)
Тут есть базовый класс, который говорит: «Вот, смотрите, есть такой метод, он виртуальный». А дальше приходят его дети-наследники и такие: «Папаша, твой метод — говно, мы сделаем по-своему». И переопределяют его. В итоге ты вызываешь метод у папы, а работает реализация ребёнка. Магия, блядь.
public class Document
{
// Базовый класс, метод виртуальный — можно переопределить
public virtual void Print() => Console.WriteLine("Печать базового документа.");
}
public class PdfDocument : Document
{
// А вот сынок уже умный, говорит: «Нахуй эту печать, я в PDF!»
public override void Print() => Console.WriteLine("Отправка PDF на виртуальный принтер.");
}
public class WordDocument : Document
{
// И этот тоже не лыком шит
public override void Print() => Console.WriteLine("Запуск MS Word для печати.");
}
// А теперь смотри, в чём прикол
List<Document> docs = new() { new Document(), new PdfDocument(), new WordDocument() };
foreach (var doc in docs)
{
doc.Print(); // Вызывается у всех одна и та же штука, но работает по-разному!
}
Вот ты смотришь на список документов, для тебя они все просто Document, но когда дело доходит до печати — каждый делает свою дичь. PDF не печатает, а отправляет куда-то, Word — запускает целое приложение. А ты сидишь и не паришься, кто там внутри.
2. Полиморфизм через интерфейсы (или «договорились на берегу»)
А это когда классы вообще не родственники, но они оба подписали одну бумажку — интерфейс. Типа: «Ладно, мужики, вы можете быть кем угодно — хоть файлом, хоть консолью, — но метод Log у вас будет, договорились?» И все идут и делают.
public interface ILogger
{
void Log(string message); // Контракт, блядь. Подписался — делай.
}
public class FileLogger : ILogger
{
// Этот пишет в файл, тихий такой, трудяга
public void Log(string message) => File.AppendAllText("log.txt", message);
}
public class ConsoleLogger : ILogger
{
// А этот орет на всю консоль
public void Log(string message) => Console.WriteLine($"[LOG] {message}");
}
// И вот ты пишешь метод, который принимает любого, кто подписал контракт ILogger
public void ProcessOrder(ILogger logger)
{
logger.Log("Начало обработки заказа."); // А ему похуй, кто это — лишь бы Log был
// ... дальше какая-то логика
}
Красота в чём? Захотел сменить логгер с файла на базу данных — написал новый класс DatabaseLogger, который тоже реализует ILogger, и подсунул его в метод. Весь остальной код даже не заметит подмены, потому что он работает с интерфейсом, а не с конкретной реализацией. Гениально и просто, как три копейки.
3. Перегрузка методов (или «один имя, много лиц»)
Это когда у тебя в одном классе несколько методов с одинаковым именем, но разными параметрами. Компилятор сам разберётся, какой из них вызывать, в зависимости от того, что ты ему суёшь.
public class Calculator
{
public int Add(int a, int b) => a + b; // Для целых чисел
public double Add(double a, double b) => a + b; // Для дробных
public string Add(string a, string b) => a + b; // А это для строк — конкатенация, ёпта
}
Ты вызываешь Add, а система смотрит: ага, два int — значит, первый метод. Два double — второй. Две строки — третий. Удобно, не нужно выдумывать кучу разных имён вроде AddInts, AddDoubles, ConcatStrings. Всё называется Add, и всем хорошо.
Так в чём, блядь, выгода?
А выгода в том, что твой код становится гибким, как жопа гимнастки. Ты пишешь систему, которая работает с абстракцией (базовый класс Document или интерфейс ILogger), а потом можешь добавлять новые конкретные реализации (ExcelDocument, TelegramLogger) — и основная логика программы не меняется ни на йоту. Она продолжает тупо вызывать Print() или Log(), а новенькие классы сами разберутся, что делать.
Это и есть снижение связанности: твой высокоуровневый код не зависит от низкоуровневых деталей. Он просто знает, что есть некий контракт, и ему всё равно, кто и как его выполняет. А если нужно что-то новое — просто написал новый класс, реализовал тот же интерфейс, и вперёд. Без правок в старом коде, без риска всё сломать. Красота, да и только.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶