Menu
CoddyTech

向上转型与向下转型

本课属于 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 模式匹配,因为它能将类型检查和强制转换合并为一步,让你的代码更安全、更简洁。

challenge icon

挑战

简单

让我们构建一个车辆检查系统,演示如何通过向上转型和向下转型,灵活地处理继承层次结构中的对象。你将创建不同类型的车辆,并编写代码来安全地识别特定类型的车辆并与之交互。

你将把代码分布在 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()
    }
}
quiz icon自我测试

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

面向对象编程中的所有课

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