Menu
Coddy logo textTech

Полиморфизм: компиляция vs выполнение

Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 25 из 70.

Полиморфизм означает «многообразие форм» — способность одного и того же кода вести себя по-разному в зависимости от контекста. C# поддерживает два различных типа полиморфизма, и понимание того, когда разрешается каждый из них, помогает писать более гибкий код.

Полиморфизм на этапе компиляции (также называемый статическим полиморфизмом) разрешается компилятором до запуска вашей программы. Перегрузка методов является основным примером — несколько методов имеют одно и то же имя, но разные параметры:

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
}

var calc = new Calculator();
calc.Add(5, 3);       // Компилятор выбирает версию int
calc.Add(5.0, 3.0);   // Компилятор выбирает версию double

Компилятор анализирует типы аргументов и выбирает соответствующий метод на этапе компиляции. Это решение фиксируется еще до того, как программа начнет выполняться.

Полиморфизм времени выполнения (динамический полиморфизм) разрешается во время выполнения программы. Это происходит с методами virtual и override — фактический тип объекта определяет, какой метод будет выполнен:

Animal pet = new Dog();  // Объявлен как Animal, фактически Dog
pet.Speak();             // Вызывает Speak() класса Dog во время выполнения

Компилятор не знает, какой именно Speak() будет запущен — это решение принимается во время выполнения на основе истинного типа объекта. Именно это делает полиморфизм на основе наследования таким мощным инструментом для создания гибких и расширяемых систем.

challenge icon

Задание

Легко

Давайте создадим систему конвертации, которая демонстрирует оба типа полиморфизма в действии. Вы создадите класс, использующий перегрузку методов (полиморфизм времени компиляции), наряду с иерархией наследования, использующей виртуальные/переопределенные методы (полиморфизм времени выполнения).

Вы организуете свой код в четырех файлах:

  • Converter.cs: Определите класс Converter в пространстве имен Conversion. Этот класс демонстрирует полиморфизм времени компиляции через перегрузку методов. Создайте три перегруженных метода Convert:
    • Convert(int value) возвращает "Integer: {value}"
    • Convert(double value) возвращает "Double: {value}"
    • Convert(string value) возвращает "String: {value}"
  • Shape.cs: Определите базовый класс Shape в пространстве имен Conversion. Этот класс имеет свойство Name (string) и конструктор, который его устанавливает. Включите virtual метод с именем Describe(), который возвращает "This is a {Name}".
  • Circle.cs: Определите класс Circle в пространстве имен Conversion, который наследуется от Shape. Добавьте свойство Radius (double). Конструктор принимает радиус и передает "Circle" в базовый конструктор. Переопределите Describe(), чтобы он возвращал "This is a Circle with radius {Radius}".
  • Program.cs: В основном файле продемонстрируйте оба типа полиморфизма. Сначала создайте Converter и вызовите все три перегруженных метода с входными значениями. Затем создайте Circle, сохраните его в переменной типа Shape и вызовите Describe(), чтобы показать полиморфизм времени выполнения.

Вы получите четыре входных значения:

  • Целое число (integer)
  • Число с плавающей точкой (double)
  • Строковое значение (string)
  • Радиус круга (double)

Выведите результат в следующем формате:

Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}

Например, если входные данные — 42, 3.14, Hello и 5.5, вывод должен быть следующим:

Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5

Обратите внимание на ключевое различие: компилятор решает, какой метод Convert вызвать, основываясь на типах аргументов во время компиляции, в то время как метод Describe() разрешается во время выполнения на основе фактического типа объекта, даже если он хранится в переменной Shape!

Шпаргалка

Полиморфизм означает «много форм» — способность одного и того же кода вести себя по-разному в зависимости от контекста.

Полиморфизм времени компиляции (статический полиморфизм) разрешается компилятором до запуска программы. Перегрузка методов является основным примером:

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
}

var calc = new Calculator();
calc.Add(5, 3);       // Компилятор выбирает версию int
calc.Add(5.0, 3.0);   // Компилятор выбирает версию double

Компилятор проверяет типы аргументов и выбирает нужный метод во время компиляции.

Полиморфизм времени выполнения (динамический полиморфизм) разрешается во время работы программы с использованием методов virtual и override:

Animal pet = new Dog();  // Объявлено как Animal, на самом деле Dog
pet.Speak();             // Вызывает Speak() класса Dog во время выполнения

Фактический тип объекта определяет, какой метод будет выполнен во время выполнения, а не объявленный тип переменной.

Попробуйте сами

using System;
using Conversion;

class Program
{
    public static void Main(string[] args)
    {
        // Чтение входных данных
        int intValue = Convert.ToInt32(Console.ReadLine());
        double doubleValue = Convert.ToDouble(Console.ReadLine());
        string stringValue = Console.ReadLine();
        double radius = Convert.ToDouble(Console.ReadLine());

        // TODO: Продемонстрировать полиморфизм времени компиляции
        // Создать объект Converter и вызвать все три перегруженных метода Convert
        Console.WriteLine("Compile-time Polymorphism:");
        // Вызвать Convert с intValue, doubleValue и stringValue

        // TODO: Продемонстрировать полиморфизм времени выполнения
        // Создать объект Circle и сохранить его в переменной типа Shape
        // Затем вызвать Describe() для переменной Shape
        Console.WriteLine("Runtime Polymorphism:");
        // Создать Circle и вызвать Describe()
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование