C#如何实现接口和抽象类 C#接口与抽象类区别和用法

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

接口和抽象类都是C#中实现多态和代码复用的重要机制,但它们定位不同:接口定义“能做什么”,抽象类定义“是什么”以及“怎么做的一部分”。选哪个不看语法多酷,而要看设计意图。

接口(interface):契约式能力声明

接口只包含成员签名(方法、属性、事件、索引器),不带实现,也不允许字段、构造函数或访问修饰符(默认public)。从C# 8.0起支持默认方法实现,但本质仍是契约优先。

  • 一个类可实现多个接口,解决C#单继承限制
  • 适合定义跨领域、无继承关系的统一行为,比如IComparableIDisposableIAsyncEnumerable
  • 典型写法:public interface ILogger { void Log(string message); }
  • 实现时必须显式提供所有成员的具体逻辑,除非使用默认实现且不重写

抽象类(abstract class):半成品基类

抽象类可含抽象成员(无实现,强制子类重写)、具体成员(带实现)、字段、构造函数、甚至静态成员。它表达一种“类型归属”——子类本质上是它的特殊化。

  • 一个类只能继承一个抽象类,但可同时实现多个接口
  • 适合有共用状态或基础逻辑的场景,比如StreamException等框架基类
  • 典型写法: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
  • 是否需要共享字段或构造逻辑?→ 只能用抽象类
  • 不确定时,先定义接口;后续发现共性逻辑再引入抽象基类(接口+抽象实现类组合很常见)

基本上就这些。接口管“协议”,抽象类管“谱系”。用对了,代码松耦合又易维护;混着乱用,后期改起来才真要命。