home commit with new plugins
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
# 抽象类和接口的区别
|
||||
概念:
|
||||
抽象类和接口都可以用来做抽象,都能配合多态使用
|
||||
但它们表达的语义不一样
|
||||
|
||||
简单理解:
|
||||
抽象类更像“是什么”
|
||||
接口更像“能做什么”
|
||||
|
||||
例如:
|
||||
1. `Animal` 抽象类:表示狗、猫、鸟都是动物
|
||||
2. `IFlyable` 接口:表示某个对象具备飞行能力
|
||||
|
||||
----
|
||||
# 先看抽象类
|
||||
抽象类:
|
||||
```csharp
|
||||
abstract class Animal
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public Animal(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public void Eat()
|
||||
{
|
||||
Console.WriteLine(Name + "吃东西");
|
||||
}
|
||||
|
||||
public abstract void Speak();
|
||||
}
|
||||
```
|
||||
|
||||
子类:
|
||||
```csharp
|
||||
class Dog : Animal
|
||||
{
|
||||
public Dog(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Speak()
|
||||
{
|
||||
Console.WriteLine(Name + "汪汪");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
抽象类适合:
|
||||
1. 有明确父子关系
|
||||
2. 要复用公共字段、构造函数、普通方法
|
||||
3. 还要强制子类实现某些行为
|
||||
|
||||
一句话:
|
||||
抽象类适合表达“这类东西的共同基础”
|
||||
|
||||
----
|
||||
# 再看接口
|
||||
接口:
|
||||
```csharp
|
||||
interface IFlyable
|
||||
{
|
||||
void Fly();
|
||||
}
|
||||
```
|
||||
|
||||
实现类:
|
||||
```csharp
|
||||
class Bird : IFlyable
|
||||
{
|
||||
public void Fly()
|
||||
{
|
||||
Console.WriteLine("鸟飞");
|
||||
}
|
||||
}
|
||||
|
||||
class Plane : IFlyable
|
||||
{
|
||||
public void Fly()
|
||||
{
|
||||
Console.WriteLine("飞机飞");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
接口适合:
|
||||
1. 表达一种能力
|
||||
2. 多个无关类型都能实现同一种行为
|
||||
3. 一个类需要同时拥有多种能力
|
||||
4. 调用方只关心能力,不关心具体身份
|
||||
|
||||
一句话:
|
||||
接口适合表达“谁能做这件事”
|
||||
|
||||
----
|
||||
# 核心区别
|
||||
| 对比点 | 抽象类 | 接口 |
|
||||
|---|---|---|
|
||||
| 关键词 | `abstract class` | `interface` |
|
||||
| 主要表达 | 是什么 | 能做什么 |
|
||||
| 继承数量 | 一个类只能继承一个类 | 一个类可以实现多个接口 |
|
||||
| 成员实现 | 可以有普通实现,也可以有抽象成员 | 主要规定契约,要求实现类提供成员 |
|
||||
| 字段 | 可以有字段 | 通常不放实例字段 |
|
||||
| 构造函数 | 可以有构造函数 | 不能像类一样写实例构造函数 |
|
||||
| 适合场景 | 提取共同父类和公共代码 | 表达能力、规则、插件式扩展 |
|
||||
| 多态使用 | 可以用抽象类引用指向子类 | 可以用接口引用指向实现类 |
|
||||
|
||||
----
|
||||
# 单继承和多实现
|
||||
C# 中:
|
||||
一个类只能继承一个父类
|
||||
但可以实现多个接口
|
||||
|
||||
```csharp
|
||||
abstract class Character
|
||||
{
|
||||
public string Name;
|
||||
}
|
||||
|
||||
interface IMovable
|
||||
{
|
||||
void Move();
|
||||
}
|
||||
|
||||
interface IAttackable
|
||||
{
|
||||
void Attack();
|
||||
}
|
||||
|
||||
class Player : Character, IMovable, IAttackable
|
||||
{
|
||||
public void Move()
|
||||
{
|
||||
Console.WriteLine("移动");
|
||||
}
|
||||
|
||||
public void Attack()
|
||||
{
|
||||
Console.WriteLine("攻击");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
理解:
|
||||
1. `Player` 是一种 `Character`
|
||||
2. `Player` 同时具备移动能力和攻击能力
|
||||
3. 父类写一个,接口可以写多个
|
||||
|
||||
----
|
||||
# 什么时候用抽象类
|
||||
适合用抽象类:
|
||||
```csharp
|
||||
abstract class Enemy
|
||||
{
|
||||
public int Hp;
|
||||
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
Hp -= damage;
|
||||
}
|
||||
|
||||
public abstract void Attack();
|
||||
}
|
||||
```
|
||||
|
||||
因为:
|
||||
1. 小怪、Boss、远程敌人都可以算敌人
|
||||
2. 它们都有血量、受伤逻辑
|
||||
3. 它们攻击方式不同
|
||||
|
||||
这种情况抽象类很合适:
|
||||
有共同身份,也有公共代码
|
||||
|
||||
----
|
||||
# 什么时候用接口
|
||||
适合用接口:
|
||||
```csharp
|
||||
interface IDamageable
|
||||
{
|
||||
void TakeDamage(int damage);
|
||||
}
|
||||
```
|
||||
|
||||
实现者可能是:
|
||||
```csharp
|
||||
class Player : IDamageable
|
||||
{
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Enemy : IDamageable
|
||||
{
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Box : IDamageable
|
||||
{
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
玩家、敌人、箱子不是同一种父类关系,但它们都可以“受伤/受击”。
|
||||
|
||||
这种情况接口更合适:
|
||||
没有共同身份,但有共同能力
|
||||
|
||||
----
|
||||
# 可以一起用
|
||||
抽象类和接口不是二选一,经常会一起使用。
|
||||
|
||||
```csharp
|
||||
abstract class Character
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public Character(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public abstract void Update();
|
||||
}
|
||||
|
||||
interface IAttackable
|
||||
{
|
||||
void Attack();
|
||||
}
|
||||
|
||||
class Warrior : Character, IAttackable
|
||||
{
|
||||
public Warrior(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
Console.WriteLine(Name + "更新状态");
|
||||
}
|
||||
|
||||
public void Attack()
|
||||
{
|
||||
Console.WriteLine(Name + "挥剑攻击");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
理解:
|
||||
1. `Warrior` 是一种 `Character`
|
||||
2. `Warrior` 具备 `IAttackable` 能力
|
||||
3. 抽象类负责共同身份和基础逻辑
|
||||
4. 接口负责额外能力
|
||||
|
||||
----
|
||||
# 和多态的关系
|
||||
抽象类多态:
|
||||
```csharp
|
||||
Animal animal = new Dog();
|
||||
animal.Speak();
|
||||
```
|
||||
|
||||
接口多态:
|
||||
```csharp
|
||||
IFlyable flyable = new Bird();
|
||||
flyable.Fly();
|
||||
```
|
||||
|
||||
它们都能做到:
|
||||
用统一类型接收不同具体对象
|
||||
运行时执行不同实现
|
||||
|
||||
区别在于:
|
||||
1. 抽象类变量强调“它属于某个父类体系”
|
||||
2. 接口变量强调“它拥有某个能力”
|
||||
|
||||
----
|
||||
# 常见判断方法
|
||||
问自己几个问题:
|
||||
|
||||
1. 这些类是不是同一种东西?
|
||||
- 是:优先考虑抽象类
|
||||
- 不是:优先考虑接口
|
||||
2. 是否需要复用字段、构造函数、普通方法?
|
||||
- 是:抽象类更合适
|
||||
3. 一个类是否需要同时拥有多种能力?
|
||||
- 是:接口更合适
|
||||
4. 调用方真正关心的是身份还是能力?
|
||||
- 关心身份:抽象类
|
||||
- 关心能力:接口
|
||||
|
||||
----
|
||||
# 常见误区
|
||||
误区1:接口就是更高级的抽象类
|
||||
|
||||
不是。
|
||||
接口和抽象类不是上下级关系,而是用途不同
|
||||
|
||||
----
|
||||
误区2:有公共方法就一定用抽象类
|
||||
|
||||
不一定。
|
||||
如果这些类型没有真正的父子关系,硬抽象成父类可能违反[[里氏替换原则]]
|
||||
|
||||
----
|
||||
误区3:接口只能有方法
|
||||
|
||||
接口也可以有属性、索引器、事件等成员。
|
||||
初学阶段先重点掌握方法和属性即可。
|
||||
|
||||
----
|
||||
# 注意
|
||||
1. 抽象类和接口都不能直接创建对象
|
||||
2. 抽象类用 `abstract class`,接口用 `interface`
|
||||
3. 抽象类适合表达共同身份和公共代码
|
||||
4. 接口适合表达能力和契约
|
||||
5. 类只能继承一个父类,但可以实现多个接口
|
||||
6. 抽象类和接口都可以配合多态
|
||||
7. 设计时先看语义,不要只看语法
|
||||
|
||||
----
|
||||
# 一句话记忆
|
||||
==抽象类管“是谁的一种”,接口管“能做什么”。==
|
||||
|
||||
再压缩一点:
|
||||
==同类用抽象类,能力用接口。==
|
||||
|
||||
----
|
||||
# 面试/复习时怎么说
|
||||
可以这样答:
|
||||
|
||||
“抽象类和接口都能表达抽象,也都可以配合多态使用。
|
||||
抽象类更强调父子关系和公共代码复用,一个类只能继承一个抽象类;接口更强调能力或契约,一个类可以实现多个接口。
|
||||
如果多个类本质上是同一种东西,并且有公共字段、构造函数或普通方法,可以用抽象类;如果只是多个类型都有同一种能力,通常用接口更合适。”
|
||||
|
||||
----
|
||||
引用:
|
||||
1. Microsoft Learn: [abstract (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract)
|
||||
2. Microsoft Learn: [interface (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface)
|
||||
3. Microsoft Learn: [Interfaces](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces)
|
||||
|
||||
#抽象类
|
||||
#接口
|
||||
#核心
|
||||
Reference in New Issue
Block a user