home commit with new plugins

This commit is contained in:
2026-06-21 09:51:55 +08:00
parent ebb9e385c5
commit 3d067fe75c
85 changed files with 10794 additions and 1443 deletions
+476
View File
@@ -0,0 +1,476 @@
# 接口
概念:
接口使用 `interface` 关键字定义
它主要用来规定一组==能力 / 规则 / 契约==
简单理解:
接口不关心“你是谁”
只关心“你能不能做这件事”
例如:
`IFlyable` 表示“会飞”
不管你是鸟、飞机还是飞行道具,只要实现了这个接口,就代表你拥有飞行能力
通常用法:
1. 规定某些类必须实现哪些成员
2. 表达一种能力,例如会飞、会攻击、可保存、可移动
3. 让不同类型用同一套调用方式
4. 弥补 C# 类只能单继承的问题,因为一个类可以实现多个接口
5. 配合[[多态vob|多态]],让代码面向能力编程
----
# 语法
定义接口:
```csharp
interface
{
void ();
}
```
实现接口:
```csharp
class :
{
public void ()
{
}
}
```
命名习惯:
接口名通常以大写字母 `I` 开头
例如:`IFlyable``IAttackable``ISaveable`
----
# 基础案例
```csharp
interface IFlyable
{
void Fly();
}
class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("鸟在飞");
}
}
class Plane : IFlyable
{
public void Fly()
{
Console.WriteLine("飞机在飞");
}
}
void LetItFly(IFlyable flyable)
{
flyable.Fly();
}
LetItFly(new Bird());
LetItFly(new Plane());
```
理解:
1. `IFlyable` 规定实现者必须有 `Fly()` 方法
2. `Bird``Plane` 类型不同,但都实现了 `IFlyable`
3. `LetItFly` 不关心传进来的是鸟还是飞机,只关心它会不会飞
这就是接口常见用法:
==面向能力编程,而不是面向具体类型编程==
----
# 接口中的成员
常见接口成员:
```csharp
interface IMovable
{
void Move();
int Speed { get; set; }
}
```
实现:
```csharp
class Player : IMovable
{
public int Speed { get; set; }
public void Move()
{
Console.WriteLine("玩家移动");
}
}
```
接口中常见成员包括:
1. 方法
2. 属性
3. 索引器
4. 事件
初学阶段重点先掌握:
接口方法 + 接口属性
----
# 接口成员默认是 public
接口里这样写:
```csharp
interface IAttackable
{
void Attack();
}
```
实现时必须写 `public`
```csharp
class Enemy : IAttackable
{
public void Attack()
{
Console.WriteLine("敌人攻击");
}
}
```
错误写法:
```csharp
class Enemy : IAttackable
{
void Attack()
{
}
}
```
原因:
接口成员代表对外公开的契约
类实现接口时,成员访问级别不能比接口更低
----
# 一个类可以实现多个接口
C# 的类只能继承一个父类,但可以实现多个接口。
```csharp
interface IMovable
{
void Move();
}
interface IAttackable
{
void Attack();
}
class Player : IMovable, IAttackable
{
public void Move()
{
Console.WriteLine("玩家移动");
}
public void Attack()
{
Console.WriteLine("玩家攻击");
}
}
```
理解:
`Player` 同时拥有“可移动”和“可攻击”两种能力
----
# 继承类和实现接口同时存在
写法:
```csharp
class : , 1, 2
{
}
```
注意顺序:
==先写父类,再写接口==
案例:
```csharp
class Character
{
public string Name;
}
interface IAttackable
{
void Attack();
}
class Warrior : Character, IAttackable
{
public void Attack()
{
Console.WriteLine(Name + "挥剑攻击");
}
}
```
错误示意:
```csharp
// class Warrior : IAttackable, Character
// {
// }
```
如果既要继承类,又要实现接口,父类必须写在最前面。
----
# 接口也可以继承接口
接口之间可以继承,而且可以继承多个接口。
```csharp
interface IMovable
{
void Move();
}
interface IAttackable
{
void Attack();
}
interface IPlayerAction : IMovable, IAttackable
{
void UseItem();
}
```
实现 `IPlayerAction` 的类,必须实现:
1. `Move()`
2. `Attack()`
3. `UseItem()`
```csharp
class Player : IPlayerAction
{
public void Move()
{
}
public void Attack()
{
}
public void UseItem()
{
}
}
```
----
# 接口引用和多态
接口也可以当变量类型使用。
```csharp
IAttackable attacker = new Player();
attacker.Attack();
```
也可以作为方法参数:
```csharp
void DoAttack(IAttackable attacker)
{
attacker.Attack();
}
```
好处:
调用方只依赖 `IAttackable`
任何实现了这个接口的对象都能传进来
```csharp
DoAttack(new Player());
DoAttack(new Enemy());
```
这也是多态的一种表现。
----
# 接口和里氏替换原则
接口常用来拆分能力,避免不合理继承。
比如:
```csharp
abstract class Bird
{
public string Name;
}
interface IFlyable
{
void Fly();
}
class Sparrow : Bird, IFlyable
{
public void Fly()
{
Console.WriteLine("麻雀飞");
}
}
class Penguin : Bird
{
public void Swim()
{
Console.WriteLine("企鹅游泳");
}
}
```
这样:
1. `Bird` 只表达“鸟”
2. `IFlyable` 只表达“会飞”
3. 不会飞的企鹅不用硬实现 `Fly()`
这个例子可以和[[里氏替换原则]]一起理解。
----
# 显式实现接口
当两个接口有同名方法,或者你不希望方法直接暴露在类对象上,可以显式实现接口。
```csharp
interface IWalkable
{
void Move();
}
interface IFlyable
{
void Move();
}
class Bird : IWalkable, IFlyable
{
void IWalkable.Move()
{
Console.WriteLine("鸟在走");
}
void IFlyable.Move()
{
Console.WriteLine("鸟在飞");
}
}
Bird bird = new Bird();
IWalkable walkable = bird;
walkable.Move();
IFlyable flyable = bird;
flyable.Move();
```
注意:
显式实现接口时,不写 `public`
调用时通常要先把对象当成对应接口类型
初学阶段先知道有这个写法即可,最常用的还是普通实现接口。
----
# 常见错误
错误1:实现接口时忘记 `public`
```csharp
class Player : IAttackable
{
void Attack()
{
}
}
```
接口成员默认是公开契约,实现时通常要写 `public`
----
错误2:没有实现接口所有成员
```csharp
interface IMovable
{
void Move();
int Speed { get; set; }
}
class Player : IMovable
{
public void Move()
{
}
}
```
`Speed` 没实现,所以会报错。
----
错误3:把接口当成类直接创建对象
```csharp
IFlyable flyable = new IFlyable();
```
接口只是规则,不能直接 `new`
正确写法:
```csharp
IFlyable flyable = new Bird();
```
----
# 什么时候用接口
适合用接口的情况:
1. 你要表达的是“能力”,不是“父子身份”
2. 多个无关类型都有同一种行为
3. 一个类型需要拥有多种能力
4. 想让代码依赖规则,而不是依赖具体类
例如:
- `IFlyable`:会飞
- `IAttackable`:能攻击
- `ISaveable`:可保存
- `IDamageable`:可受伤
----
# 注意
1. 接口用 `interface` 定义
2. 接口名通常以 `I` 开头
3. 接口不能直接创建对象
4. 类和结构体都可以实现接口
5. 一个类可以实现多个接口
6. 接口成员实现时通常要写 `public`
7. 接口更适合表达“能做什么”,抽象类更适合表达“是什么”
8. 接口常和多态、里氏替换原则一起理解
----
# 一句话记忆
==接口是能力契约,谁实现它,谁就必须按规则提供这些能力。==
再压缩一点:
==接口管能不能做,不管你是谁。==
----
# 面试/复习时怎么说
可以这样答:
“接口是用 `interface` 定义的一组契约,主要用来规定实现类必须提供哪些成员。
类可以实现多个接口,所以接口常用来表达能力,比如可移动、可攻击、可保存。
调用方可以面向接口编程,只依赖能力规则,不依赖具体类,这样更利于扩展和多态。”
----
引用:
1. Microsoft Learn: [interface (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface)
2. Microsoft Learn: [Interfaces](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces)
#接口
#核心