Ответ
Ad-hoc полиморфизм (полиморфизм специальных случаев) — это способность функции или оператора иметь разные реализации для разных типов аргументов, определяемые на этапе компиляции. В C# он реализуется через перегрузку методов (method overloading) и перегрузку операторов (operator overloading).
Ключевое отличие от параметрического полиморфизма (generics):
- Ad-hoc: Поведение метода разное для каждого типа (
int,string). Компилятор выбирает конкретную реализацию на основе типов аргументов. - Generics: Поведение метода одинаковое (универсальное) для всех подставляемых типов.
Пример перегрузки методов:
public class Logger
{
// Разные реализации для разных типов входных данных
public void Log(int number)
{
Console.WriteLine($"[INFO] Integer: {number}");
}
public void Log(string message)
{
Console.WriteLine($"[INFO] String: {message}");
}
public void Log(string message, LogLevel level) // Перегрузка по количеству параметров
{
Console.WriteLine($"[{level}] {message}");
}
}
// Использование
var logger = new Logger();
logger.Log(42); // Вызовет Log(int)
logger.Log("Hello"); // Вызовет Log(string)
logger.Log("Error!", LogLevel.Error); // Вызовет Log(string, LogLevel)
Пример перегрузки операторов (ad-hoc полиморфизм для операторов):
public struct Vector2D
{
public double X { get; }
public double Y { get; }
public Vector2D(double x, double y) { X = x; Y = y; }
// Определяем поведение оператора '+' для нашего типа
public static Vector2D operator +(Vector2D a, Vector2D b)
{
return new Vector2D(a.X + b.X, a.Y + b.Y);
}
}
var v1 = new Vector2D(1, 2);
var v2 = new Vector2D(3, 4);
var sum = v1 + v2; // Будет использована наша перегруженная реализация оператора
Практическая ценность: Ad-hoc полиморфизм повышает читаемость кода, позволяя использовать одно и то же осмысленное имя метода (Log, Calculate, Draw) для операций над разными типами данных.
Ответ 18+ 🔞
А, слушай, вот есть такая штука — ad-hoc полиморфизм, или, по-нашему, полиморфизм специальных случаев. Это когда функция или оператор ведут себя по-разному для разных типов данных, и компилятор ещё на этапе сборки решает, какую именно реализацию выбрать. В C# это делается через перегрузку методов и перегрузку операторов.
Чем отличается от generics (параметрического полиморфизма)?
- Ad-hoc: Для каждого типа — своя, отдельная реализация. Для
int— одна логика, дляstring— другая. Компилятор смотрит на типы аргументов и тыкает пальцем: "Ага, вот эту функцию берём". - Generics: Логика одна и та же, универсальная, просто тип подставляется как параметр. Как шаблон, блядь.
Пример перегрузки методов:
public class Logger
{
// Для целых чисел — одна история
public void Log(int number)
{
Console.WriteLine($"[INFO] Integer: {number}");
}
// Для строк — уже другая
public void Log(string message)
{
Console.WriteLine($"[INFO] String: {message}");
}
// А тут ещё и по количеству параметров перегрузили
public void Log(string message, LogLevel level)
{
Console.WriteLine($"[{level}] {message}");
}
}
// Использование
var logger = new Logger();
logger.Log(42); // Вызовет Log(int) — работает с числами
logger.Log("Hello"); // Вызовет Log(string) — работает со строками
logger.Log("Error!", LogLevel.Error); // Вызовет Log(string, LogLevel) — третий вариант
Пример перегрузки операторов (это тоже ad-hoc полиморфизм, ёпта):
public struct Vector2D
{
public double X { get; }
public double Y { get; }
public Vector2D(double x, double y) { X = x; Y = y; }
// Говорим компилятору: "Слышь, для нашего типа Vector2D оператор '+' работает вот так"
public static Vector2D operator +(Vector2D a, Vector2D b)
{
return new Vector2D(a.X + b.X, a.Y + b.Y);
}
}
var v1 = new Vector2D(1, 2);
var v2 = new Vector2D(3, 4);
var sum = v1 + v2; // Используется наша, кастомная реализация, а не стандартная
В чём профит, спросишь? Читаемость, блядь! Вместо того чтобы плодить методы вроде LogInteger, LogString, LogStringWithLevel, можно использовать одно осмысленное имя — Log. И все сразу понимают, что происходит, даже не вдаваясь в детали. Код становится чище, а жизнь — немного светлее, хоть и ненадолго.