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() 访问基类实现。
挑战
简单让我们构建一个通知系统,展示派生类如何使用 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() 方法并打印结果
// 由于运行时多态,程序会调用被重写的方法
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器