6.6 KiB
继承
概念: 继承是面向对象三大特性之一 可以让一个类在已有类的基础上继续扩展 被继承的类一般叫==父类 / 基类(base class)== 继承别人的类一般叫==子类 / 派生类(derived class)==
通常用法: 1. 提取多个类的共同内容,减少重复代码 2. 表达“==is-a==”关系 3. 让子类在父类基础上增加自己的成员和功能
语法:
class 子类名 : 父类名
{
}
案例:
class Animal
{
public string name;
protected int age;
public Animal(string name)
{
this.name = name;
}
public void Speak()
{
Console.WriteLine("动物发出声音");
}
}
class Dog : Animal
{
public Dog(string name, int age) : base(name)
{
this.age = age;
}
public void Bark()
{
Console.WriteLine(name);
Console.WriteLine(age);
Console.WriteLine("汪汪");
}
}
Dog d = new Dog("旺财", 3);
d.Speak();
d.Bark();
↑ Dog : Animal 就表示 Dog 继承了 Animal
注意:
- ==C# 的类只能直接继承一个父类==,也就是单继承
- 继承可以传递,A 继承 B,B 继承 C,那么 A 也会拥有 C 中可继承的成员
- ==构造函数不会被继承==,子类只是可以调用父类构造函数
- 父类的
private成员子类==不能直接访问==,想给子类用一般写protected - 结构体
struct==不支持继承类==,但可以实现接口 - 子类对象本质上也是父类对象的一种,所以继承是后面学多态vob和里氏替换原则的基础
继承中的构造函数
概念:
继承里最容易混淆的一点就是==“子类会继承父类成员,但不会继承父类构造函数”==
当你 new 一个子类对象时,不只是创建“子类自己的部分”,还要先把==父类那一部分==初始化好
所以子类构造函数最终一定会调用某个父类构造函数
通常用法: 1. 让父类负责初始化“大家共有”的数据 2. 让子类只负责补充自己的初始化逻辑 3. 保证对象创建出来时就是完整、可用的状态
核心规则:
1. ==父类构造函数不会被继承==
2. ==子类构造函数最终一定会调用某个父类构造函数==
3. 如果子类没有显式写 : base(...),编译器会尝试补一个 : base()
4. 如果父类没有无参构造函数,子类就必须显式写 : base(...)
5. 构造函数体的执行顺序可以先记成:==先父类,后子类==
案例1:显式调用父类构造函数
class Person
{
public string Name;
public Person(string name)
{
Name = name;
Console.WriteLine("Person构造");
}
}
class Student : Person
{
public int Score;
public Student(string name, int score) : base(name)
{
Score = score;
Console.WriteLine("Student构造");
}
}
Student s = new Student("小明", 100);
执行理解:
1. 先执行 Person(string name)
2. 再执行 Student(string name, int score) 的函数体
案例2:父类有无参构造函数时,可以省略 base()
class Animal
{
public Animal()
{
Console.WriteLine("Animal构造");
}
}
class Dog : Animal
{
public Dog()
{
Console.WriteLine("Dog构造");
}
}
Dog d = new Dog();
↑ 这里虽然没写 : base(),但编译器会默认帮你补上
案例3:父类没有无参构造函数时,子类必须显式写 base(...)
class Animal
{
public string Name;
public Animal(string name)
{
Name = name;
}
}
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
错误示意:
class Dog : Animal
{
public Dog()
{
}
}
↑ 这时会报错,因为编译器默认想调用 base(),但父类根本没有无参构造函数
案例4:子类构造函数之间也可以先用 this(...) 串起来
class Animal
{
public string Name;
public Animal(string name)
{
Name = name;
}
}
class Dog : Animal
{
public int Age;
public Dog() : this("默认小狗", 0)
{
}
public Dog(string name, int age) : base(name)
{
Age = age;
}
}
理解:
Dog() 没有直接 base(name),而是先调用同类里的 Dog(string name, int age)
然后再由那个构造函数去调用 base(name)
所以最终调用链还是会到父类构造函数
注意:
- 一个构造函数初始化器里==只能写一个==,也就是写了
this(...)就不能再写base(...) - 但
this(...)调到的另一个构造函数里,仍然可以写base(...) - 不要在子类里重复初始化父类已经负责的数据,父类自己的数据最好交给父类构造函数处理
- 初学阶段先牢牢记住:==创建子类对象时,一定先把父类部分构造好==
base关键字
概念:
base 用来在==子类内部==访问父类成员
最常见的用途就是调用父类构造函数
通常用法: 1. 在子类构造函数中调用父类构造函数 2. 在子类方法中访问父类同名成员或父类方法
语法:
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
调用父类成员:
base.父类成员名
案例:
class Father
{
public string name;
public Father(string name)
{
this.name = name;
}
public void Say()
{
Console.WriteLine("我是父类");
}
}
class Son : Father
{
public Son(string name) : base(name)
{
}
public void Test()
{
base.Say();
Console.WriteLine(base.name);
}
}
Son s = new Son("小李");
s.Test();
↑ base(name) 是先调用父类构造函数,base.Say() 是调用父类方法
注意:
base只能在==实例构造函数、实例方法、实例属性访问器==里用- ==静态方法里不能用
base== - 如果父类没有无参构造函数,子类通常就要显式写
: base(...) base是访问父类成员,不是创建一个新的父类对象
引用:
- Microsoft Learn: Inheritance - derive types to create more specialized behavior
- Microsoft Learn: Inheritance in C# and .NET
- Microsoft Learn: Constructors (C# Programming Guide)
- Microsoft Learn: Using constructors
- Microsoft Learn: base (C# Reference)
- Microsoft Learn: protected (C# Reference)
#继承 #核心