Полиморфизм в C#

Полиморфизм в C#: один интерфейс — разные реализации, virtual/override, абстрактные классы и приведение типов.

Полиморфизм — способность объектов разных классов отвечать на одно и то же обращение по-своему. Это позволяет писать обобщённый код, который работает с разными типами без if-лесенок.

Полиморфизм через virtual/override

class Notification
{
    public virtual void Send(string message)
    {
        Console.WriteLine($"Уведомление: {message}");
    }
}

class EmailNotification : Notification
{
    public override void Send(string message)
    {
        Console.WriteLine($"[Email] {message}");
    }
}

class SmsNotification : Notification
{
    public override void Send(string message)
    {
        Console.WriteLine($"[SMS] {message}");
    }
}

// Обобщённый код — не знает конкретный тип
Notification[] notifiers = {
    new EmailNotification(),
    new SmsNotification(),
    new Notification()
};

foreach (var n in notifiers)
    n.Send("Ваш заказ готов!");

Вывод:

[Email] Ваш заказ готов!
[SMS] Ваш заказ готов!
Уведомление: Ваш заказ готов!

Массив объявлен как Notification[], но при вызове Send каждый объект ведёт себя по-своему — это и есть полиморфизм.

Абстрактные классы

Абстрактный класс нельзя создать через new — он служит только основой. Абстрактные методы не имеют тела; потомок обязан их реализовать:

abstract class Employee
{
    public string Name { get; set; }
    public Employee(string name) { Name = name; }

    public abstract decimal CalculateSalary();  // каждый считает по-своему

    public void PrintSalary()
    {
        Console.WriteLine($"{Name}: {CalculateSalary():C}");
    }
}

class FullTimeEmployee : Employee
{
    private decimal _monthlySalary;
    public FullTimeEmployee(string name, decimal monthly) : base(name)
        => _monthlySalary = monthly;

    public override decimal CalculateSalary() => _monthlySalary;
}

class Contractor : Employee
{
    private decimal _hourlyRate;
    private int _hours;
    public Contractor(string name, decimal rate, int hours) : base(name)
        { _hourlyRate = rate; _hours = hours; }

    public override decimal CalculateSalary() => _hourlyRate * _hours;
}

Employee[] staff = {
    new FullTimeEmployee("Алиса", 80000),
    new Contractor("Боб", 1500, 40)
};

foreach (var e in staff)
    e.PrintSalary();

Вывод:

Алиса: 80 000,00 ₽
Боб: 60 000,00 ₽

Проверка типа: is и as

Notification n = new EmailNotification();

if (n is EmailNotification email)
    Console.WriteLine("Это email: " + email.GetType().Name);

// as — мягкое приведение; null если тип не совпал
SmsNotification? sms = n as SmsNotification;
Console.WriteLine(sms == null ? "Не SMS" : "Это SMS");

Вывод:

Это email: EmailNotification
Не SMS

Коротко

  • Полиморфизм: разные классы по-своему реализуют один метод; код работает с базовым типом.
  • abstract class нельзя создать через new; абстрактные методы обязан реализовать потомок.
  • is проверяет тип и приводит в одной строке; as возвращает null вместо исключения.
Проверьте себя
1. Что такое полиморфизм?
AВозможность создавать много объектов
BСпособность объектов разных классов реагировать на одно обращение по-своему
CНаследование более чем от одного класса
DСкрытие полей класса
2. Можно ли создать объект абстрактного класса напрямую через new?
AДа, как обычного класса
BНет, только через потомков
CДа, если все методы реализованы
DТолько в статическом контексте
3. Что вернёт выражение: obj is SomeClass x — если obj не является SomeClass?
Aисключение
Bfalse, x будет null/default
Ctrue
Dошибку компиляции
Поддержать проект