Menu
Coddy logo textTech

Ключевые слова Virtual и Override

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

Когда производный класс наследует метод, иногда требуется полностью изменить поведение этого метода. Ключевые слова virtual и override работают вместе, чтобы сделать это возможным контролируемым образом.

Пометьте метод как virtual в базовом классе, чтобы указать, что производным классам разрешено переопределять его реализацию:

public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Some sound");
    }
}

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

Ключевое слово override в производном классе сообщает компилятору, что вы намеренно заменяете базовый метод. Без virtual в базовом методе вы не можете использовать override.

Настоящая мощь проявляется тогда, когда вы используете ссылку на базовый класс для хранения производного объекта:

Animal myPet = new Dog();
myPet.Speak();  // Вывод: Woof!

Несмотря на то, что myPet объявлена как Animal, C# вызывает версию Speak() класса Dog. Это называется полиморфизмом времени выполнения — фактически вызываемый метод зависит от истинного типа объекта, а не от объявленного типа переменной.

Вы все еще можете получить доступ к базовой реализации, используя base.Speak() внутри вашего переопределения, если вы хотите расширить, а не полностью заменить поведение.

challenge icon

Задание

Легко

Давайте создадим систему уведомлений, которая демонстрирует, как производные классы могут полностью переопределять поведение базового класса с помощью ключевых слов virtual и override, а также покажем мощь полиморфизма времени выполнения.

Вы создадите три файла для организации вашего кода:

  • Notification.cs: Определите класс Notification в пространстве имен Alerts. Этот базовый класс представляет собой общее уведомление со свойством Message (string) и конструктором, который его устанавливает. Включите virtual метод с именем Send(), который возвращает "Sending notification: {Message}".
  • EmailNotification.cs: Определите класс EmailNotification в пространстве имен Alerts, который наследуется от Notification. Добавьте свойство Recipient (string) и конструктор, принимающий как сообщение, так и получателя (используйте base для передачи сообщения). Переопределите метод Send(), чтобы он возвращал "Emailing {Recipient}: {Message}".
  • SmsNotification.cs: Определите класс SmsNotification в пространстве имен Alerts, который также наследуется от Notification. Добавьте свойство PhoneNumber (string) и конструктор, принимающий как сообщение, так и номер телефона. Переопределите Send(), чтобы он возвращал "SMS to {PhoneNumber}: {Message}".
  • Program.cs: В основном файле создайте экземпляры всех трех типов уведомлений, используя входные значения. Затем продемонстрируйте полиморфизм времени выполнения, сохранив все три экземпляра в переменных типа Notification и вызвав Send() для каждого из них.

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

  • Сообщение уведомления
  • Адрес электронной почты получателя
  • Номер телефона для SMS

Выведите результат вызова Send() для каждого уведомления, сохраненного как ссылка на базовый класс:

{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}

Например, если входные данные: Meeting at 3pm, alice@email.com и 555-1234, вывод должен быть следующим:

Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pm

Обратите внимание, что хотя все три объекта хранятся в переменных типа Notification, каждый из них вызывает свою собственную переопределенную версию Send(). Это и есть полиморфизм времени выполнения в действии!

Шпаргалка

Используйте ключевое слово virtual в базовом классе, чтобы пометить метод, который производные классы могут переопределить:

public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Some sound");
    }
}

Используйте ключевое слово override в производном классе, чтобы заменить реализацию метода базового класса:

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

Полиморфизм времени выполнения возникает, когда ссылка на базовый класс содержит производный объект — вызываемый метод зависит от истинного типа объекта, а не от объявленного типа переменной:

Animal myPet = new Dog();
myPet.Speak();  // Output: Woof!

Вы можете получить доступ к базовой реализации внутри переопределения, используя base:

public override void Speak()
{
    base.Speak();  // Вызывает версию базового класса
    Console.WriteLine("Woof!");
}

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

using System;
using Alerts;

class Program
{
    public static void Main(string[] args)
    {
        // Чтение входных данных
        string message = Console.ReadLine();
        string email = Console.ReadLine();
        string phone = Console.ReadLine();

        // TODO: Создайте экземпляры всех трех типов уведомлений
        // Сохраните их в переменные типа Notification для демонстрации полиморфизма
        
        // TODO: Создайте базовое уведомление Notification
        Notification baseNotification;
        
        // TODO: Создайте EmailNotification (сохраненное как тип Notification)
        Notification emailNotification;
        
        // TODO: Создайте SmsNotification (сохраненное как тип Notification)
        Notification smsNotification;
        
        // TODO: Вызовите Send() для каждого и выведите результаты
        // Переопределенные методы будут вызваны благодаря полиморфизму времени выполнения
    }
}
quiz iconПроверьте себя

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

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