1. 入门
这节课我们来聊聊C#里的继承语法:学会怎么声明派生类,明白base关键字是啥,以及怎么用它访问父类。这样不仅能让你写代码更轻松,还能让你的程序优雅不少(有时候还能多睡会儿——毕竟不用修复制粘贴的bug了)。
举个生活中的简单例子:我们有个Vehicle类(交通工具)——这是个基础、通用的蓝图。所有交通工具都能开动,都有颜色、品牌啥的:
public class Vehicle
{
public string Brand { get; set; }
public string Color { get; set; }
public void Drive()
{
Console.WriteLine("开动啦!");
}
}
现在我们想加一种新交通工具——汽车。汽车还是交通工具,但它有自己的特点(比如门的数量)。如果再写一遍颜色和品牌就太奇怪了,这些已经有啦!
继承让我们可以说:“汽车是交通工具,只不过有更多花样。”
2. 怎么声明子类
在C#里,创建继承类用冒号:。在类名后面写上你要继承的类。
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
}
可以这样理解: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); // 错误!看不到。
}
}
对比:基类 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(下节课详细聊)。
最重要的:继承很方便,但别搞太复杂的继承链。现实里,如果你的继承链超过两三层,大概率哪里出问题了。很容易搞不清谁继承谁,最后自己都晕了。
GO TO FULL VERSION