接口定义“能做什么”,抽象类定义“是什么”及“怎么做的一部分”;接口仅含成员签名、支持多实现,抽象类可含字段和实现、仅单继承,选择取决于设计意图而非语法。

接口和抽象类都是C#中实现多态和代码复用的重要机制,但它们定位不同:接口定义“能做什么”,抽象类定义“是什么”以及“怎么做的一部分”。选哪个不看语法多酷,而要看设计意图。
接口(interface):契约式能力声明
接口只包含成员签名(方法、属性、事件、索引器),不带实现,也不允许字段、构造函数或访问修饰符(默认public)。从C# 8.0起支持默认方法实现,但本质仍是契约优先。
- 一个类可实现多个接口,解决C#单继承限制
- 适合定义跨领域、无继承关系的统一行为,比如IComparable、IDisposable、IAsyncEnumerable
- 典型写法:public interface ILogger { void Log(string message); }
- 实现时必须显式提供所有成员的具体逻辑,除非使用默认实现且不重写
抽象类(abstract class):半成品基类
抽象类可含抽象成员(无实现,强制子类重写)、具体成员(带实现)、字段、构造函数、甚至静态成员。它表达一种“类型归属”——子类本质上是它的特殊化。
- 一个类只能继承一个抽象类,但可同时实现多个接口
- 适合有共用状态或基础逻辑的场景,比如Stream、Exception等框架基类
- 典型写法:public abstract class Shape { public abstract double Area { get; } public virtual void Draw() => Console.WriteLine("Drawing..."); }
- 子类用override重写抽象成员,用base.调用父类已实现方法
关键区别速查表
能否实例化?都不能直接new,但抽象类可被继承后实例化,接口需由实现类实例化。
成员实现?接口成员默认无实现(C#8+可有默认实现,但不改变其契约本质);抽象类可混用抽象与具体成员。
字段/构造函数?接口不能有字段或构造函数;抽象类可以。
访问修饰符?接口成员隐式public,不可加private/protected;抽象类成员可设protected、internal等。
版本演进?给接口新增成员会破坏已有实现类(需全部修改);抽象类可加非抽象成员并提供默认行为,更易扩展。
怎么选?看这三个问题
- 是否想表达“属于同一类事物”?→ 优先抽象类(如Animal → Dog/Cat)
- 是否想让无关类型共享某项能力?→ 优先接口(如File、Network、Memory都可支持IReadable)
- 是否需要共享字段或构造逻辑?→ 只能用抽象类
- 不确定时,先定义接口;后续发现共性逻辑再引入抽象基类(接口+抽象实现类组合很常见)
基本上就这些。接口管“协议”,抽象类管“谱系”。用对了,代码松耦合又易维护;混着乱用,后期改起来才真要命。








