CodeGym /课程 /C# SELF /C#里的继承语法和 base

C#里的继承语法和 base

C# SELF
第 20 级, 课程 1
可用

1. 入门

这节课我们来聊聊C#里的继承语法:学会怎么声明派生类,明白base关键字是啥,以及怎么用它访问父类。这样不仅能让你写代码更轻松,还能让你的程序优雅不少(有时候还能多睡会儿——毕竟不用修复制粘贴的bug了)。

举个生活中的简单例子:我们有个Vehicle类(交通工具)——这是个基础、通用的蓝图。所有交通工具都能开动,都有颜色、品牌啥的:

public class Vehicle
{
    public string Brand { get; set; }
    public string Color { get; set; }

    public void Drive()
    {
        Console.WriteLine("开动啦!");
    }
}
基础类 Vehicle

现在我们想加一种新交通工具——汽车。汽车还是交通工具,但它有自己的特点(比如门的数量)。如果再写一遍颜色和品牌就太奇怪了,这些已经有啦!

继承让我们可以说:“汽车是交通工具,只不过有更多花样。”

2. 怎么声明子类

在C#里,创建继承类用冒号:。在类名后面写上你要继承的类。


public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }
}
声明继承类 Car

可以这样理解:Car类继承了Vehicle类的所有字段和方法,还加了自己的东西——比如门的数量。

注意:C#只支持单继承(也就是一个类只能有一个父类)。不过接口随便继承多少都行!(后面会讲到)。

怎么运行的?

当你写下:

Car myCar = new Car();

……你的myCar对象就能访问Vehicle的所有字段/方法,还有自己的属性。比如:

myCar.Brand = "Toyota";
myCar.Color = "蓝色";
myCar.NumberOfDoors = 4;
myCar.Drive(); // 继承来的方法

实际场景:
在我们“进化中的”应用里,可以做个交通工具菜单,让用户选类型,然后程序显示它的所有属性。加新类型时你只用写最少的代码!

3. 继承构造函数

创建派生类对象时,有时候想通过构造函数初始化基类的属性。比如,确保一定要填品牌和颜色:


public class Vehicle
{
    public string Brand { get; set; }
    public string Color { get; set; }

    public Vehicle(string brand, string color)
    {
        Brand = brand;
        Color = color;
    }
}

现在在子类构造函数里可以(也应该)调用基类构造函数!用base关键字就行:


public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }

    public Car(string brand, string color, int numberOfDoors)
        : base(brand, color) // 调用Vehicle的构造函数!
    {
        NumberOfDoors = numberOfDoors;
    }
}

实际效果长啥样?

Car bmw = new Car("BMW", "黑色", 4);
Console.WriteLine($"{bmw.Brand}, {bmw.Color}, 门数: {bmw.NumberOfDoors}");
bmw.Drive(); // 还是能用!

所以bmw既是汽车,也是交通工具。它记得自己的“父母”。

4. base和this的区别

C#里有两个关键字用来操作类成员:this和base。

  • this——指当前对象(比如自己的字段或方法)。
  • base——指基类的成员(也就是父类)。

什么时候用base?比如你想扩展基类方法的逻辑,或者想在派生类里初始化基类。比如:


public class Bicycle : Vehicle
{
    public int NumberOfGears { get; set; }

    public Bicycle(string brand, string color, int gears)
        : base(brand, color)    // 调用基类构造函数
    {
        NumberOfGears = gears;
    }

    public void ShowInfo()
    {
        // 用基类的属性!
        Console.WriteLine($"{Brand} ({Color}), 变速数: {NumberOfGears}");
    }
}

5. 方法里的base关键字

base不只是用在构造函数!有时候你想改基类方法的行为,但又想用它的一部分逻辑。

比如我们想让Car类的Drive方法还能输出我们开的是什么车:


public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }

    public Car(string brand, string color, int numberOfDoors) 
        : base(brand, color)
    {
        NumberOfDoors = numberOfDoors;
    }

    public void DriveCar()
    {
        base.Drive(); // 调用基类实现
        Console.WriteLine($"开着{Brand},有{NumberOfDoors}个门!");
    }
}

解释:
调用base.Drive();就像说:“爸,你先讲你的部分,我再补充细节!”所以调用DriveCar()时会输出:

开动啦!
开着BMW,有4个门!

方法重写(override)我们下节课再聊 :P

6. 继承关系图

来画个类之间的关系图。简单点(UML太吓人,尤其早上!):

Vehicle(基类)
   /         \
Car      Bicycle
  • 所有箭头都是从子类指向父类。
  • 所有子类都能访问public/protected的基类成员。

public、protected和private的区别

小提醒,别搞混了。
在派生类里能看到:

修饰符 子类可见性
public 能
protected 能
private 不能

常见错误:
试图在子类里访问private字段:


public class Vehicle
{
    private string secret = "谁也不知道!";
}

public class Car : Vehicle
{
    public void RevealSecret()
    {
        Console.WriteLine(secret); // 错误!看不到。
    }
}
private——只有基类能用,家族秘密不外传!

对比:基类 vs. 派生类

Vehicle Car(子类)
Brand 有 继承
Color 有 继承
Drive() 有 继承(或扩展)
NumberOfDoors 没有 自己的(独有属性)

7. 示例

如果我们继续开发简单的“交通工具管理”应用,可以这样写:


public class Vehicle
{
    public string Brand { get; set; }
    public string Color { get; set; }

    public Vehicle(string brand, string color)
    {
        Brand = brand;
        Color = color;
    }

    public void Drive()
    {
        Console.WriteLine($"{Brand} {Color} 开动啦!");
    }
}

public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }

    public Car(string brand, string color, int numberOfDoors)
        : base(brand, color)
    {
        NumberOfDoors = numberOfDoors;
    }

    public void ShowCar()
    {
        Console.WriteLine($"品牌: {Brand}, 颜色: {Color}, 门数: {NumberOfDoors}");
    }
}

public class Bicycle : Vehicle
{
    public int NumberOfGears { get; set; }

    public Bicycle(string brand, string color, int numberOfGears)
        : base(brand, color)
    {
        NumberOfGears = numberOfGears;
    }
}

在主方法里可以创建交通工具列表,各种描述它们:

Car ford = new Car("Ford", "红色", 4);
ford.ShowCar();
ford.Drive();

Bicycle trek = new Bicycle("Trek", "绿色", 21);
trek.Drive();

8. 错误、细节,还有点幽默

超级常见的错误——在子类里忘了调用基类构造函数。如果Vehicle没有无参构造函数,而你又没写base(...),编译器就会报错:它说无法初始化基类。就像想盖二楼却没一楼,根本不行。得把数据往上交。

第二个错误:忘了基类里private修饰的字段/方法在子类里是看不到的。如果想让“孩子”能用,用protected(下节课详细聊)。

最重要的:继承很方便,但别搞太复杂的继承链。现实里,如果你的继承链超过两三层,大概率哪里出问题了。很容易搞不清谁继承谁,最后自己都晕了。

2
任务
C# SELF, 第 20 级, 课程 1
已锁定
使用基类构造函数的继承
使用基类构造函数的继承
2
任务
C# SELF, 第 20 级, 课程 1
已锁定
使用关键字 `base` 调用基类方法
使用关键字 `base` 调用基类方法
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION