Menu
CoddyTech

virtual 和 override 关键字

本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 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# 会调用 Dog 版本的 Speak()。这称为运行时多态:实际调用的方法取决于对象的真实类型,而不是变量声明的类型。

如果你想扩展原有行为,而不是完全替换它,仍然可以在重写的方法中使用 base.Speak() 访问基类实现。

challenge icon

挑战

简单

让我们构建一个通知系统,展示派生类如何使用 virtual 和 override 关键字完全重写基类的行为,同时展现运行时多态的强大之处。

你将创建 3 个文件来组织代码:

  • Notification.cs:在 Alerts 命名空间中定义一个 Notification 类。这个基类表示通用通知,包含一个 Message 属性(字符串)和一个用于设置该属性的构造函数。添加一个名为 Send() 的 virtual 方法,使其返回 "Sending notification: {Message}"。
  • EmailNotification.cs:在 Alerts 命名空间中定义一个 EmailNotification 类,使其继承自 Notification。添加一个 Recipient 属性(字符串)和一个同时接收消息与收件人的构造函数(使用 base 传递消息)。重写 Send() 方法,使其返回 "Emailing {Recipient}: {Message}"。
  • SmsNotification.cs:在 Alerts 命名空间中定义一个 SmsNotification 类,使其同样继承自 Notification。添加一个 PhoneNumber 属性(字符串)和一个同时接收消息与电话号码的构造函数。重写 Send(),使其返回 "SMS to {PhoneNumber}: {Message}"。
  • Program.cs:在主文件中,使用输入值创建这 3 种通知类型的实例。然后,将这 3 个实例都存储在 Notification 类型的变量中,并对每个实例调用 Send(),以展示运行时多态。

你将收到 4 个输入:

  • 通知消息
  • 电子邮件收件人地址
  • 用于 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

请注意,尽管这 3 个实例都存储在 Notification 类型的变量中,但每个实例都会调用自己重写后的 Send() 版本。这就是运行时多态的实际效果!

亲手试试

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自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C# 在线编译器