在 C# 面向对象编程中,抽象类(abstract class) 与 接口(interface) 是实现“抽象化设计”的两大核心工具。
二者都用于隐藏实现细节、统一行为规范,但在语言能力、继承模型、设计目标上存在本质差异。
一句话总览:
抽象类 = 对“类体系”的抽象
接口 = 对“能力 / 行为”的抽象
一、核心定义与基础特性
1.1 抽象类(abstract class):对“高度相关对象共性”的抽象
抽象类用于表达 “是什么(is-a)” 的关系,适合描述一组在语义、职责和行为上高度相关的对象。
核心特征
- 可包含:
--> 字段(状态)
--> 构造函数
--> 已实现方法
--> 抽象方法 - 不可实例化
- 只要包含抽象成员,类必须声明为
abstract - 子类:
--> 必须实现所有抽象成员
--> 或继续声明为抽象类
示例:动物模型(高度相关对象)
public abstract class Animal
{
protected double Weight;
protected Animal(double weight)
{
Weight = weight;
}
public void Eat()
{
Console.WriteLine($"体重 {Weight}kg 的动物正在进食");
}
public abstract void MakeSound();
}
public class Dog : Animal
{
public Dog(double weight) : base(weight) { }
public override void MakeSound()
{
Console.WriteLine("小狗汪汪叫");
}
}
class Program
{
static void Main()
{
Dog dog = new Dog(5);
dog.Eat();
dog.MakeSound();
}
}
输出:
体重 5kg 的动物正在进食
小狗汪汪叫
📌 关键点
抽象类非常适合用于提炼多个高度相关对象之间的共性,并承载共享状态和基础实现。
1.2 接口(interface):对“行为能力”的抽象
接口用于描述对象“能做什么(can-do)”,强调行为契约而非对象之间的内在关系。
核心特征(按版本理解)
C# 8.0 之前
- 只能包含:
--> 方法 / 属性 / 事件 / 索引器的声明 - 无字段、无构造函数
- 所有成员隐式
public - 实现类必须 完全实现 所有成员
C# 8.0 之后(重要补充)
- 允许:
--> 默认实现方法
-->private / protected成员(仅供接口内部复用) - 仍然:
--> 不能定义实例字段
--> 不能定义构造函数 - 默认实现 ≠ 状态共享,仅是方法层面的兜底逻辑
示例:飞行能力(跨对象能力)
// 定义可飞行的接口,包含飞行行为和飞行提示方法
public interface IFlyable
{
// 飞行行为的抽象方法(需由实现类具体实现)
void Fly();
// 接口的默认实现方法:显示通用飞行提示
void ShowFlyTip()
{
Console.WriteLine("飞行需遵循空气动力学原理");
}
}
// 鸟类类,实现IFlyable接口
public class Bird : IFlyable
{
// 实现IFlyable接口的Fly方法:定义鸟类的飞行方式
public void Fly()
{
Console.WriteLine("鸟类通过翅膀扇动飞行");
}
}
// 飞机类,实现IFlyable接口
public class Plane : IFlyable
{
// 实现IFlyable接口的Fly方法:定义飞机的飞行方式
public void Fly()
{
Console.WriteLine("飞机通过引擎推力飞行");
}
// 重写接口的默认方法ShowFlyTip:自定义飞机的飞行提示
public void ShowFlyTip()
{
Console.WriteLine("飞机飞行需遵循航空管制规则");
}
}
// 主程序类
class Program
{
// 程序入口方法
static void Main()
{
// 实例化鸟类对象并调用飞行方法和提示方法
IFlyable bird = new Bird();
bird.Fly();
bird.ShowFlyTip();
// 实例化飞机对象并调用飞行方法和提示方法
IFlyable plane = new Plane();
plane.Fly();
plane.ShowFlyTip();
}
}
输出:
鸟类通过翅膀扇动飞行
飞行需遵循空气动力学原理
飞机通过引擎推力飞行
飞机飞行需遵循航空管制规则
📌 关键点
接口非常适合表达不相关对象之间的共同能力,用于解耦和能力组合。
二、核心差异深度对比(语法 + 设计)
差异 1:是否支持状态(字段)
| 抽象类 | 接口 | |
|---|---|---|
| 字段 | ✅ 支持 | ❌ 不支持 |
| 状态共享 | ✅ | ❌ |
结论:
- 需要
状态 + 行为→ 抽象类 - 只需要
行为规范→ 接口
差异 2:成员修饰符的自由度
| 抽象类 | 接口 | |
|---|---|---|
| public / protected | ✅ | ⚠️ 有限制 |
| private | ✅ | C# 8+(仅接口内部使用) |
| 📌 面试纠正常见误区 |
“接口成员不能加修饰符”
✔ C# 8 之前正确
✔ C# 8 之后不完全正确
差异 3:继承模型(本质差异)
// ❌ 错误:C# 不支持类多继承
class Student : Person, Athlete { }
// ✅ 正确:1 个抽象类 + 多个接口
class Student : Person, IStudy, IExercise { }
| 抽象类 | 接口 | |
|---|---|---|
| 继承数量 | 单继承 | 多实现 |
| 设计意义 | 共性约束 | 能力叠加 |
差异 4:是否支持“部分实现”
抽象类:支持分阶段实现
// 抽象基类:动物(定义所有动物的核心行为)
public abstract class Animal
{
// 抽象方法:移动(由子类实现具体方式)
public abstract void Move();
// 抽象方法:呼吸(由子类实现具体方式)
public abstract void Breathe();
}
// 抽象子类:陆生动物(继承Animal,实现通用移动逻辑)
public abstract class TerrestrialAnimal : Animal
{
// 重写:陆生动物通用移动方式
public override void Move()
{
Console.WriteLine("陆生动物通过四肢移动");
}
}
// 具体类:猫(继承陆生动物,实现猫的呼吸方式)
public class Cat : TerrestrialAnimal
{
// 重写:猫的呼吸方式
public override void Breathe()
{
Console.WriteLine("猫通过肺呼吸");
}
}
// 主程序(程序入口)
class Program
{
static void Main()
{
// 实例化猫(仅具体类可实例化)
Cat myCat = new Cat();
myCat.Move(); // 调用陆生动物的Move方法
myCat.Breathe(); // 调用猫的Breathe方法
// 多态:抽象类引用具体对象
Animal animal = new Cat();
animal.Move();
animal.Breathe();
}
}
输出:
陆生动物通过四肢移动
猫通过肺呼吸
陆生动物通过四肢移动
猫通过肺呼吸
接口:实现类必须完整实现
// 可移动接口:定义可移动对象的核心行为(移动、停止)
public interface IMovable
{
// 抽象方法:移动(由实现类定义具体移动逻辑)
void Move();
// 抽象方法:停止(由实现类定义具体停止逻辑)
void Stop();
}
// 自行车类:实现IMovable接口,定义自行车的移动和停止行为
public class Bicycle : IMovable
{
// 实现移动方法:自行车前进逻辑
public void Move() => Console.WriteLine("自行车前进");
// 实现停止方法:自行车刹车逻辑
public void Stop() => Console.WriteLine("自行车刹车");
}
// 主程序类:程序入口
class Program
{
static void Main()
{
// 实例化自行车对象
IMovable bicycle = new Bicycle();
// 调用移动方法
bicycle.Move();
// 调用停止方法
bicycle.Stop();
}
}
输出:
自行车前进
自行车刹车
📌
- 抽象类:允许“未完成的模板”
- 接口:必须提供完整能力实现
三、设计哲学(最容易被忽略的核心)
抽象类:自上而下(Top-down)
- 先抽象领域中的核心概念
- 再派生具体实现
- 强调 对象之间的内在关联与一致性
常见场景
- 领域模型(DDD)
- 框架或模块基类(Controller / DbContext)
接口:自下而上(Bottom-up)
- 先出现具体需求
- 再抽象共同行为
- 强调解耦、组合与扩展性
常见场景:
- 架构设计
- 依赖注入(DI)
- 插件 / 扩展机制
四、最终选型口诀(工程实践版)
- 是否需要共享状态?
→ 是:抽象类
→ 否:接口 - 是否存在明确的 is-a 关系?
→ 是:抽象类
→ 否:接口 - 是否需要多继承能力?
→ 是:接口 - 是否用于解耦、扩展或依赖注入?
→ 接口
总结
- 抽象类用于抽象一组高度相关对象的共性、状态和部分实现,强调
is-a - 接口用于抽象跨对象的行为能力,强调
can-do - 抽象类支持字段、构造函数和分阶段实现
- 接口支持多实现,天然适合解耦和扩展
- 优先使用接口,必要时使用抽象类 是现代 C# 设计的普遍共识
1186

被折叠的 条评论
为什么被折叠?



