Что такое ad-hoc полиморфизм?

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

Ответ

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) для операций над разными типами данных.