- 示例工程
【免费下载链接】awesome-low-level-design
Learn Low Level Design (LLD) and prepare for interviews using free resources.
本指南围绕 awesome-low-level-design 仓库中 oop/csharp/interfaces/README.md 的核心内容展开,系统讲解 C# 接口的定义、实现、多继承与默认方法(C# 8),并结合仓库内真实源码(适配器、工厂、策略等设计模式示例)展示接口在低层设计(LLD)与面试场景中的实战价值。读完本文,你将掌握接口的完整语法、设计原则、与抽象类的取舍,以及如何用接口写出高内聚、低耦合、易于替换与测试的代码。
一、接口(Interface)是什么
在面向对象编程(OOP)中,接口是一组方法定义的集合,它本身不提供任何实现,而是为实现它的类定义一份必须遵守的"契约"(Contract)。接口是 C# 实现抽象(Abstraction)、多态(Polymorphism)与松散耦合(Loose Coupling)三大核心特性的基石。
与类不同,接口关注的是"一个对象能做什么(what it does)",而不是"它如何做(how it does it)"——实现细节完全交给实现类,调用方只需面向接口编程即可。
二、C# 接口的关键特性
根据原文档,C# 中的接口具备以下核心特征:
- 定义契约:接口规定了实现类必须提供的成员,是类与外部调用方之间的协议。
- 成员默认公开且抽象:接口成员隐式地为
public且没有实现体(除非使用 C# 8 起的默认实现)。 - 不能包含实例字段:接口中只允许声明静态字段和常量,不能保存对象实例状态。
- 支持多继承:一个类可以实现多个接口,弥补了 C# 类只支持单继承的限制。
- 提升代码灵活性与可维护性:面向接口编程让替换实现、扩展功能、单元测试都变得更加容易。
补充说明:接口成员默认是
public的,且不允许声明访问修饰符(C# 8 默认实现方法除外);接口也不能包含构造函数。
三、定义与实现接口:三步实战
Step 1:使用interface关键字定义接口
// Defining an interface public interface IVehicle { void Start(); // Abstract method (no implementation) void Stop(); // Abstract method (no implementation) }IVehicle声明了两个抽象方法签名Start()与Stop(),任何实现它的类都必须为这两个方法提供具体逻辑。
Step 2:使用:符号实现接口
类通过冒号(:)语法实现接口:
// Implementing the IVehicle interface in a Car class public class Car : IVehicle { public void Start() { Console.WriteLine("Car is starting..."); } public void Stop() { Console.WriteLine("Car is stopping..."); } }Car类必须为Start()和Stop()提供带public修饰符的实现——这正是"契约"的约束力所在:漏掉任何一个方法,编译期就会报错。
Step 3:通过接口引用使用实现类(多态)
public class Program { public static void Main(string[] args) { IVehicle myCar = new Car(); // Polymorphism: Interface reference myCar.Start(); myCar.Stop(); } }关键点在于IVehicle myCar = new Car():变量的静态类型是接口,运行时对象是Car。调用方只知道IVehicle契约,却可以驱动任意实现了该接口的对象,这就是接口多态的直接体现。
运行输出
Car is starting... Car is stopping...四、利用接口实现"多继承"
C# 的类只支持单继承(一个类只能有一个基类),但一个类可以同时实现多个接口,从而获得多种"能力契约"。
// First interface public interface IFlyable { void Fly(); } // Second interface public interface IDrivable { void Drive(); } // Implementing multiple interfaces public class FlyingCar : IFlyable, IDrivable { public void Fly() { Console.WriteLine("FlyingCar is flying..."); } public void Drive() { Console.WriteLine("FlyingCar is driving..."); } }使用示例
public class Program { public static void Main(string[] args) { FlyingCar myVehicle = new FlyingCar(); myVehicle.Fly(); myVehicle.Drive(); } }运行输出
FlyingCar is flying... FlyingCar is driving...FlyingCar同时满足"可飞行"与"可驾驶"两份契约,这正是接口组合(Interface Composition)优于继承层级的地方:它用能力维度而非"是什么"的继承树来组织行为,避免了菱形继承等复杂问题。
五、接口默认方法(C# 8)
C# 8 起,接口中允许声明带方法体的默认方法(Default Interface Methods),在保持向后兼容的同时,让接口可以承载公共逻辑。
public interface IAnimal { void Sound(); // Default method with implementation public void Sleep() { Console.WriteLine("Sleeping..."); } } public class Dog : IAnimal { public void Sound() { Console.WriteLine("Dog barks"); } }Dog只实现了必选的Sound(),Sleep()则直接复用接口提供的默认实现;实现类也可以按需override默认方法提供自定义行为。
使用示例
public class Program { public static void Main(string[] args) { Dog myDog = new Dog(); myDog.Sound(); myDog.Sleep(); // Calling default method } }运行输出
Dog barks Sleeping...使用建议:默认方法适合为接口"增量演进"提供兼容性,例如在已发布接口上新增能力;但过度依赖默认方法会使接口退化为带状态的基类,设计新接口时仍应以纯抽象契约为优先。
六、真实业务案例:支付系统
原文档给出了一个经典的支付场景:用IPayment接口统一抽象"付款"行为,让多种支付渠道各自实现。
public interface IPayment { void Pay(double amount); } public class CreditCardPayment : IPayment { public void Pay(double amount) { Console.WriteLine($"Paid {amount} using Credit Card"); } } public class PayPalPayment : IPayment { public void Pay(double amount) { Console.WriteLine($"Paid {amount} using PayPal"); } }使用示例
public class Program { public static void Main(string[] args) { IPayment payment1 = new CreditCardPayment(); payment1.Pay(100.50); IPayment payment2 = new PayPalPayment(); payment2.Pay(200.75); } }运行输出
Paid 100.5 using Credit Card Paid 200.75 using PayPal新增WeChatPayment、BankTransferPayment时,业务代码无需任何改动——它们只要实现IPayment即可接入系统,这正体现了开闭原则(Open-Closed Principle):对扩展开放,对修改关闭。
七、仓库源码佐证:接口在真实设计模式中的落地
awesome-low-level-design 仓库的 design-patterns/csharp 目录下,接口被大量应用于真实的设计模式示例,可直接对照学习。
1. 适配器模式中的支付处理器契约
IPaymentProcessor.cs 定义了一个比上例更完整的支付契约:
public interface IPaymentProcessor { void ProcessPayment(double amount, string currency); bool IsPaymentSuccessful(); string GetTransactionId(); }InHousePaymentProcessor.cs 实现该接口并写入真实逻辑(生成Guid交易号、捕获异常、维护成功状态);而 CheckoutService.cs 则通过构造函数注入持有IPaymentProcessor:
public CheckoutService(IPaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; }在 Program.cs 中,同一个CheckoutService分别注入InHousePaymentProcessor与LegacyGatewayAdapter,输出两种支付渠道的结算结果——接口让"服务"与"具体支付渠道"彻底解耦,新增渠道只加实现类,不改业务代码。
2. 工厂模式中的通知契约
INotification.cs 定义了统一的Send(string message)契约,EmailNotification、SMSNotification、PushNotification各自实现。对照 NotificationServiceNaive.cs 中"以if/else分支硬编码创建具体类"的反例写法,可以直观感受到接口+多态如何消除分支逻辑、提升可扩展性。
3. 策略模式中的运费计算契约
IShippingStrategy.cs 定义double CalculateCost(Order order),而 ShippingCostService.cs 将策略接口注入构造器,运行时动态替换不同运费算法(如按距离、按重量、按第三方 API 报价)。
以上三处源码都遵循同一模式:接口定义契约 → 具体类实现契约 → 业务类只依赖接口(依赖倒置原则,DIP),这正是低层设计面试中反复考察的核心思想。
八、接口 vs 抽象类:如何选择
接口与抽象类是 C# 实现抽象的两种手段,仓库中 oop/csharp/abstraction/README.md 对二者做了系统性对比:
| 特性 | 抽象类(Abstract Class) | 接口(Interface) |
|---|---|---|
| 方法 | 可以有抽象方法与具体方法 | C# 8 之前只能有抽象方法 |
| 字段 | 可以包含成员变量 | 不能包含实例变量 |
| 构造函数 | 可以定义构造函数 | 不能定义构造函数 |
| 多继承 | 不支持(单继承) | 支持(多接口实现) |
| 访问修饰符 | 成员可使用不同访问修饰符 | 成员默认public |
选择建议:
- 需要共享状态(字段)、构造函数或部分公共实现时,优先用抽象类;
- 需要表达"能力契约"、跨继承层级共享行为、或实现多接口组合时,优先用接口;
- 现代 C# 实践中,接口 + 组合(Composition)通常是比深继承树更受推荐的设计方式。
九、接口设计最佳实践小结
- 接口命名以
I开头,如IVehicle、IPayment、IPaymentProcessor,一眼可辨; - 接口保持精简(Interface Segregation):把大接口拆成多个小契约,让实现类只依赖它真正需要的成员;
- 面向接口编程:字段、方法参数、构造器依赖都用接口类型,便于替换实现与单元测试(可轻松注入 Mock);
- 慎用默认方法:用于兼容性演进,不用于替代正常的多态设计;
- 结合依赖注入:像仓库中的
CheckoutService、ShippingCostService那样,把接口实现通过构造函数注入,实现真正的低耦合、高内聚。
掌握 C# 接口,就等于掌握了编写可扩展、可测试、可维护代码的核心钥匙——它也是你在低层设计面试中展示设计功力的第一块敲门砖。
- 示例工程
【免费下载链接】awesome-low-level-design
Learn Low Level Design (LLD) and prepare for interviews using free resources.
相关推荐
BYOBNet架构详解:gcresnext50ts.ch_in1k如何实现灵活的神经网络配置
BYOBNet架构详解:gcresnext50ts.ch_in1k如何实现灵活的神经网络配置 gcresnext50ts.ch_in1k是一款基于BYOBNet
ramsey/uuid中的接口设计:面向契约编程实践
ramsey/uuid中的接口设计:面向契约编程实践 在现代PHP开发中,接口设计是构建灵活、可扩展系统的核心实践。ramsey/uuid作为PHP生态中处理U
后端Graphene 接口类型(Interface)深度指南:抽象字段契约、多态解析与 resolve_type 实战
Graphene 接口类型(Interface)深度指南:抽象字段契约、多态解析与 resolve_type 实战 导读 接口(Interface)是 Grap
后端API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考