C# 接口(interface)与抽象类(abstract class)的区别 - 实例讲解如何选择

来源:这里教程网 时间:2026-02-21 17:40:31 作者:

接口和抽象类都能定义契约,但用法和语义完全不同。选错会导致设计僵硬、难以扩展,甚至引发重复代码。

核心区别:职责与能力

接口描述“能做什么”(能力契约),比如

IComparable
表示“能比较大小”,
IDisposable
表示“能释放资源”。它不关心怎么实现,只规定必须提供哪些方法/属性/事件。

抽象类描述“是什么”(类型契约),代表一类事物的共性骨架。比如

Animal
抽象类可包含字段(
Age
)、已实现方法(
Breathe()
)、抽象方法(
MakeSound()
)——子类天然继承“是动物”这个身份。

语法限制决定适用场景

接口不能有字段、构造函数、析构函数,不能有访问修饰符(默认 public),所有成员隐式 abstract;C# 8.0+ 虽支持默认实现,但仍是“可选重写”,不改变其契约本质 抽象类可以有字段、构造函数、虚方法、密封方法、静态成员;子类必须通过
: base(...)
调用父类构造器,体现“is-a”关系
一个类只能继承一个抽象类,但可实现多个接口——这是选择的关键线索:需要多继承能力?选接口

实际选型判断流程

想让不同类(如
Button
File
NetworkStream
)都支持“释放资源”?→ 用
IDisposable
接口,无关类型层级
有一组紧密相关的类(
Dog
Cat
Bird
),共享状态(
Name
)、基础行为(
Eat()
)和待实现行为(
Move()
)?→ 用
Animal
抽象类
已有类层次(如
Shape
Circle
),现在要统一支持序列化?→ 不改继承链,添加
ISerializable
接口更安全
未来可能新增实现方式(比如从数据库查用户,将来还要从 API 查)?优先定义
IUserRepository
接口,便于 Mock 和替换

常见误用及修正

错误:为单个类设计抽象基类,仅为了“看起来像面向对象”——没复用、没多态必要,纯属过度设计。

错误:在接口里塞大量默认实现,把接口当抽象类用——破坏接口轻量、组合灵活的初衷。

正确做法:先问“这些类是否本质同类?”再问“它们是否需要被不同维度归类?”前者倾向抽象类,后者倾向接口。混合使用很常见:比如

Stream
是抽象类(定义字节流基本结构),同时实现
IDisposable
IAsyncDisposable
(叠加能力契约)。

基本上就这些。不复杂但容易忽略——关键不在语法,而在建模时想清楚:你在定义“角色”,还是在定义“身份”。

相关推荐

热文推荐