Files
My-Notes/笔记/CSharp/核心/抽象类和抽象方法.md
T

541 lines
10 KiB
Markdown
Raw Normal View History

2026-06-21 09:51:55 +08:00
概念:
使用 `abstract` 关键字修饰的类,叫做==抽象类==
使用 `abstract` 关键字修饰的方法,叫做==抽象方法==
简单理解:
抽象类像一份“不完整的父类模板”
它可以规定子类必须有什么能力,但具体怎么做,交给子类自己实现
补充:
有些教程会把“抽象方法”叫作“抽象函数”,初学阶段可以先理解成同一个意思
通常用法:
1. 多个类有共同身份,但父类本身不应该被直接创建对象
2. 父类能提供一部分公共代码
3. 父类还想强制子类必须实现某些方法
4. 配合[[继承]]和[[多态vob|多态]],让调用方通过父类统一调用子类行为
----
# 语法
抽象类:
```csharp
abstract class 类名
{
}
```
抽象方法:
```csharp
abstract class 类名
{
public abstract void 方法名();
}
```
子类实现抽象方法:
```csharp
class 子类名 : 抽象类名
{
public override void 方法名()
{
}
}
```
注意:
抽象方法没有方法体,结尾直接写分号 `;`
----
# 基础案例
```csharp
abstract class Animal
{
public string Name;
public Animal(string name)
{
Name = name;
}
public void Eat()
{
Console.WriteLine(Name + "正在吃东西");
}
public abstract void Speak();
}
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
public override void Speak()
{
Console.WriteLine(Name + ":汪汪");
}
}
class Cat : Animal
{
public Cat(string name) : base(name)
{
}
public override void Speak()
{
Console.WriteLine(Name + ":喵喵");
}
}
Animal a1 = new Dog("旺财");
Animal a2 = new Cat("小花");
a1.Eat();
a1.Speak();
a2.Eat();
a2.Speak();
```
理解:
1. `Animal` 是抽象类,不能直接 `new Animal()`
2. `Eat()` 是普通方法,子类可以直接继承使用
3. `Speak()` 是抽象方法,父类只规定“动物要会叫”
4. `Dog``Cat` 必须用 `override` 实现自己的 `Speak()`
5. `Animal a1 = new Dog()` 可以配合多态使用
----
# 抽象类不能直接实例化
错误写法:
```csharp
abstract class Animal
{
public abstract void Speak();
}
Animal animal = new Animal();
```
原因:
抽象类是不完整的类型,里面可能有没实现的抽象成员
所以不能直接创建对象
正确用法:
```csharp
Animal animal = new Dog("旺财");
animal.Speak();
```
----
# 抽象方法必须在子类中实现
如果子类不是抽象类,就必须实现父类中所有抽象方法。
```csharp
abstract class Shape
{
public abstract double GetArea();
}
class Circle : Shape
{
public double Radius;
public Circle(double radius)
{
Radius = radius;
}
public override double GetArea()
{
return 3.14 * Radius * Radius;
}
}
```
错误写法:
```csharp
class Circle : Shape
{
}
```
原因:
`Circle` 是普通类,但没有实现 `Shape` 要求的 `GetArea()`
----
# 子类也可以继续抽象
如果子类暂时不想实现所有抽象方法,那么这个子类也必须声明成 `abstract`
```csharp
abstract class Shape
{
public abstract double GetArea();
public abstract double GetLength();
}
abstract class RoundShape : Shape
{
public override double GetLength()
{
return 0;
}
// GetArea 暂时不实现,所以 RoundShape 也必须是抽象类
}
```
理解:
普通类必须把抽象成员补完整
抽象类可以继续把没补完的内容留给下一级子类
----
# 抽象类里可以有什么
抽象类不只是能写抽象方法,也可以写普通成员。
```csharp
abstract class Character
{
public string Name;
public int Hp;
public Character(string name, int hp)
{
Name = name;
Hp = hp;
}
public void TakeDamage(int damage)
{
Hp -= damage;
Console.WriteLine(Name + "受到了" + damage + "点伤害");
}
public virtual void Move()
{
Console.WriteLine(Name + "正在移动");
}
public abstract void Attack();
}
```
抽象类中可以有:
1. 成员变量
2. 成员属性
3. 构造函数
4. 普通方法
5. 虚方法
6. 抽象方法
7. 抽象属性、抽象索引器、抽象事件
一句话:
抽象类可以有“已经写好的公共功能”,也可以有“要求子类必须补上的功能”
----
# 抽象类可以有构造函数
抽象类不能直接 `new`,但它仍然可以有构造函数。
原因:
创建子类对象时,会先构造父类那一部分
```csharp
abstract class Enemy
{
public string Name;
public Enemy(string name)
{
Name = name;
Console.WriteLine("Enemy构造");
}
public abstract void Attack();
}
class Boss : Enemy
{
public Boss(string name) : base(name)
{
Console.WriteLine("Boss构造");
}
public override void Attack()
{
Console.WriteLine(Name + "发动攻击");
}
}
Boss boss = new Boss("魔王");
```
执行顺序还是:
1. 先执行父类 `Enemy` 构造函数
2. 再执行子类 `Boss` 构造函数
----
# abstract、virtual、override 的关系
`abstract` 方法:
```csharp
public abstract void Attack();
```
特点:
1. 没有方法体
2. 子类必须实现
3. 本身就表示允许被重写,所以子类用 `override`
`virtual` 方法:
```csharp
public virtual void Move()
{
Console.WriteLine("默认移动");
}
```
特点:
1. 有方法体
2. 子类可以选择重写,也可以不重写
3. 子类重写时也用 `override`
对比:
1. `abstract`:父类不给默认实现,子类必须改
2. `virtual`:父类给默认实现,子类可以改
3. `override`:子类真正改写父类的抽象方法或虚方法
----
# 抽象方法和普通方法的区别
普通方法:
```csharp
public void Eat()
{
Console.WriteLine("吃东西");
}
```
抽象方法:
```csharp
public abstract void Speak();
```
区别:
1. 普通方法有方法体,抽象方法没有方法体
2. 普通方法可以直接继承使用,抽象方法必须由子类实现
3. 普通方法可以写在普通类或抽象类里,抽象方法只能写在抽象类里
4. 抽象方法天然就是给多态准备的
----
# 抽象属性
抽象的不一定只有方法,也可以是属性。
```csharp
abstract class Weapon
{
public abstract int Damage { get; }
}
class Sword : Weapon
{
public override int Damage
{
get
{
return 10;
}
}
}
```
简写:
```csharp
class Bow : Weapon
{
public override int Damage => 8;
}
```
理解:
父类规定“武器必须有伤害值”
不同武器自己决定伤害值是多少
----
# 抽象类和密封类不能同时用
错误写法:
```csharp
sealed abstract class Animal
{
}
```
原因:
1. `abstract` 表示这个类必须被继承后才能补完整
2. `sealed` 表示这个类不能被继承
3. 两个要求互相矛盾
这个点可以和[[密封类]]一起记。
----
# 常见错误
错误1:抽象方法写了方法体
```csharp
abstract class Animal
{
public abstract void Speak()
{
Console.WriteLine("动物叫");
}
}
```
抽象方法不能有方法体。
----
错误2:普通类里写抽象方法
```csharp
class Animal
{
public abstract void Speak();
}
```
抽象方法只能写在抽象类中。
----
错误3:子类没实现抽象方法
```csharp
abstract class Animal
{
public abstract void Speak();
}
class Dog : Animal
{
}
```
`Dog` 如果是普通类,就必须 `override Speak()`
----
错误4:抽象方法上再写 `virtual`
```csharp
abstract class Animal
{
public abstract virtual void Speak();
}
```
抽象方法本身就表示必须被重写,不需要再写 `virtual`
----
错误5:直接 new 抽象类
```csharp
Animal animal = new Animal();
```
抽象类不能直接实例化。
----
# 什么时候用抽象类
适合用抽象类的情况:
1. 多个子类确实属于同一种父类
2. 父类有公共字段、属性、构造函数或普通方法可以复用
3. 父类还需要强制子类实现某些行为
4. 你希望调用方通过父类统一调用子类对象
例如:
- `Animal`:动物都能吃,但不同动物叫声不同
- `Shape`:图形都有面积,但不同图形面积算法不同
- `Character`:角色都有血量,但不同角色攻击方式不同
不适合用抽象类的情况:
1. 只是想规定一种能力,例如“能飞”“能攻击”
2. 多个类之间没有明确的父子关系
3. 一个类需要同时拥有多种能力
这些情况后面通常更适合用[[接口]]。
----
# 和多态的关系
抽象类经常和多态一起使用。
```csharp
abstract class Skill
{
public abstract void Release();
}
class FireSkill : Skill
{
public override void Release()
{
Console.WriteLine("释放火球");
}
}
class IceSkill : Skill
{
public override void Release()
{
Console.WriteLine("释放冰锥");
}
}
void UseSkill(Skill skill)
{
skill.Release();
}
UseSkill(new FireSkill());
UseSkill(new IceSkill());
```
理解:
1. `Skill` 规定所有技能都必须能 `Release()`
2. `FireSkill``IceSkill` 各自实现释放逻辑
3. `UseSkill` 只依赖抽象父类,不依赖具体技能类
这就是:
==抽象类定规则,多态负责运行时执行不同实现==
----
# 注意
1. 抽象类不能直接创建对象
2. 抽象方法没有方法体,结尾用分号
3. 有抽象方法的类必须是抽象类
4. 普通子类继承抽象类后,必须实现所有抽象成员
5. 抽象类可以有普通方法、字段、属性和构造函数
6. 抽象方法不能写 `private`,因为子类需要能重写它
7. 抽象方法不能同时写 `virtual`,因为它本身就是虚的
8. 抽象类不能和 `sealed` 同时使用
----
# 一句话记忆
==抽象类不能直接 new,抽象方法只定规则不写实现,子类必须 override。==
再压缩一点:
==父类抽象定规矩,子类重写补细节。==
----
# 面试/复习时怎么说
可以这样答:
“抽象类是用 `abstract` 修饰的类,不能直接实例化,主要用来作为父类。
它可以包含普通成员,也可以包含抽象成员。抽象方法没有方法体,只声明方法签名,普通子类继承后必须用 `override` 实现。
抽象类适合用来提取多个子类的共同代码,同时强制子类实现不同的行为,常和继承、多态一起使用。”
----
引用:
1. Microsoft Learn: [abstract (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract)
2. Microsoft Learn: [Abstract and Sealed Classes and Class Members](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/abstract-and-sealed-classes-and-class-members)
3. Microsoft Learn: [Inheritance - derive types to create more specialized behavior](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/inheritance)
#抽象类
#抽象方法
#核心