向上转型与向下转型
本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 29 课,共 70 课。
处理继承层次结构时,你经常需要在基类和派生类之间进行转换。向上转型会将派生类转换为基类,而向下转型则相反。
向上转型是隐式的,而且始终安全,因为可以保证派生类拥有基类的所有内容:
Dog myDog = new Dog();
Animal animal = myDog; // 向上转型:隐式进行,始终有效向下转型需要显式转换。如果对象实际上不是你要转换成的类型,运行时可能会失败:
Animal animal = new Dog();
Dog dog = (Dog)animal; // 向下转型成功,因为它实际上是 Dog 对象
Animal cat = new Cat();
Dog wrong = (Dog)cat; // 抛出 InvalidCastException 异常!要安全地向下转型,请先使用 is 关键字检查类型,或者使用 as,它会返回 null,而不是抛出异常:
if (animal is Dog d)
{
d.Bark(); // 安全:只有检查通过时才能使用 d。
}
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}在现代 C# 中,优先使用 is 模式匹配,因为它能将类型检查和强制转换合并为一步,让你的代码更安全、更简洁。
挑战
简单让我们构建一个车辆检查系统,演示如何通过向上转型和向下转型,灵活地处理继承层次结构中的对象。你将创建不同类型的车辆,并编写代码来安全地识别特定类型的车辆并与之交互。
你将把代码分布在 4 个文件中:
Vehicle.cs:在Inspection命名空间中定义基类Vehicle。每辆车都有一个LicensePlate属性(字符串),通过构造函数设置。添加一个名为GetBasicInfo()的方法,返回"Vehicle: {LicensePlate}"。Car.cs:在Inspection命名空间中定义一个继承自Vehicle的Car类。汽车还有一个额外的NumberOfDoors属性(int)。构造函数接收车牌号和车门数量。添加一个名为Honk()的方法,返回"{LicensePlate} honks: Beep beep!"。Motorcycle.cs:在Inspection命名空间中定义一个继承自Vehicle的Motorcycle类。摩托车有一个HasSidecar属性(bool)。构造函数接收车牌号和是否有边斗。添加一个名为Rev()的方法,返回"{LicensePlate} revs: Vroom!"。Program.cs:在主文件中,你将创建车辆,并练习向上转型和安全向下转型。使用输入值创建一个Car和一个Motorcycle,然后将它们都存储在一个Vehicle引用数组中(向上转型)。遍历数组,并为每辆车打印基本信息。然后使用带有模式匹配的is关键字安全地向下转型:如果它是Car,就调用Honk();如果它是Motorcycle,就调用Rev()。
你将收到 4 个输入:
- 汽车车牌号
- 车门数量
- 摩托车车牌号
- 是否有边斗(
true或false)
按以下格式打印数组中每辆车的输出:
{GetBasicInfo() result}
{Honk() or Rev() result based on actual type}例如,如果输入是 ABC-123、4、XYZ-789 和 false,输出应为:
Vehicle: ABC-123
ABC-123 honks: Beep beep!
Vehicle: XYZ-789
XYZ-789 revs: Vroom!注意,所有车辆都以 Vehicle 引用的形式存储(向上转型),但你可以使用用于向下转型的 is 模式匹配语法,安全地识别它们的实际类型并访问对应类型的方法!
亲手试试
using System;
using Inspection;
class Program
{
public static void Main(string[] args)
{
// 读取输入
string carPlate = Console.ReadLine();
int numberOfDoors = Convert.ToInt32(Console.ReadLine());
string motorcyclePlate = Console.ReadLine();
bool hasSidecar = Convert.ToBoolean(Console.ReadLine());
// TODO: 使用 carPlate 和 numberOfDoors 创建一个 Car 对象
// TODO: 使用 motorcyclePlate 和 hasSidecar 创建一个 Motorcycle 对象
// TODO: 创建一个包含这两辆车的 Vehicle 引用数组(向上转型)
// TODO: 遍历数组,对每辆车执行以下操作:
// 打印 GetBasicInfo() 的结果
// 使用 'is' 关键字结合模式匹配,安全地向下转型:
// 如果是 Car,调用并打印 Honk()
// 如果是 Motorcycle,调用并打印 Rev()
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器