你想了解 C# 中的 SOLID 五大设计原则,这是面向对象编程(OOP)中核心的设计准则,能让代码更易维护、易扩展、高内聚、低耦合,C# 作为纯 OOP 语言,对这五大原则的落地性极强。
SOLID 是五个英文单词首字母的缩写,分别对应单一职责、开闭、里氏替换、接口隔离、依赖倒置原则,下面结合 C# 的代码特点和示例,逐一清晰讲解(示例极简,聚焦原则核心):
1. 单一职责原则(Single Responsibility Principle,SRP)
核心定义 :一个类 / 方法 / 模块,应该只有一个引起它变化的原因 ,即只负责一项独立的职责。
通俗理解:一个类只做一件事,避免 "万能类""上帝类"。
C# 反例 :一个User类既负责用户信息的存储,又负责用户数据的持久化(操作数据库),还负责用户日志记录 ------ 三个职责,任意一个逻辑修改都会改动这个类,风险高。
C# 正例 :拆分出User(实体类,存信息)、UserRepository(数据持久化)、UserLogger(日志记录),每个类只负责自己的职责。
csharp
// 实体类:仅负责存储用户信息(单一职责)
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
// 数据层:仅负责用户数据的数据库操作(单一职责)
public class UserRepository
{
public void Save(User user) => Console.WriteLine("保存用户到数据库");
}
// 日志层:仅负责用户相关日志(单一职责)
public class UserLogger
{
public void LogUserOperation(string msg) => Console.WriteLine($"用户操作日志:{msg}");
}
2. 开闭原则(Open/Closed Principle,OCP)
核心定义 :软件实体(类、接口、方法)对扩展开放 ,对修改关闭。
通俗理解 :当需要新增功能时,不要修改原有稳定的代码 ,而是通过继承、实现接口、组合等方式扩展新代码。
C# 核心落地方式 :抽象(abstract)、接口(interface)、多态。
C# 反例 :一个计算商品价格的PriceCalculator类,新增 "折扣价" 功能时,直接修改原有Calculate方法,可能破坏原有 "原价计算" 逻辑。
C# 正例:定义抽象的价格计算接口,原有原价计算实现该接口,新增折扣价时只需新增一个实现类,不改动原有代码。
csharp
// 抽象接口:定义价格计算规范(稳定,不修改)
public interface IPriceCalculator
{
decimal Calculate(decimal originalPrice);
}
// 原有实现:原价计算(稳定,不修改)
public class OriginalPriceCalculator : IPriceCalculator
{
public decimal Calculate(decimal originalPrice) => originalPrice;
}
// 扩展实现:折扣价计算(新增代码,不改动原有)
public class DiscountPriceCalculator : IPriceCalculator
{
public decimal Calculate(decimal originalPrice) => originalPrice * 0.8m;
}
// 调用方:依赖抽象,支持任意扩展
public class Order
{
public decimal GetFinalPrice(IPriceCalculator calculator, decimal originalPrice)
{
return calculator.Calculate(originalPrice);
}
}
3. 里氏替换原则(Liskov Substitution Principle,LSP)
核心定义 :如果S是T的子类(派生类),那么在程序中所有使用父类T的地方 ,都可以**无感知地替换为子类S**,且不会改变程序的原有逻辑。
通俗理解 :子类必须完全继承父类的行为 ,不能破坏父类的契约(方法语义、参数 / 返回值规则、异常抛出规范),是开闭原则的基础。
C# 反例 :定义父类Bird(鸟)有Fly方法,子类Penguin(企鹅)继承Bird,但企鹅不会飞,于是重写Fly方法时抛出异常 ------ 此时用企鹅替换鸟调用Fly,程序会出错,违反里氏替换。
C# 正例 :拆分抽象,将 "会飞的鸟" 抽为IFlyingBird,普通鸟实现该接口,企鹅不实现,从根源避免契约破坏。
csharp
// 基础鸟类:定义所有鸟的公共属性(无飞行行为)
public class Bird
{
public string Name { get; set; }
public void Eat() => Console.WriteLine("鸟进食");
}
// 飞行接口:定义"会飞"的契约
public interface IFlyingBird
{
void Fly();
}
// 麻雀:会飞的鸟,实现飞行接口
public class Sparrow : Bird, IFlyingBird
{
public void Fly() => Console.WriteLine("麻雀飞翔");
}
// 企鹅:不会飞的鸟,不实现飞行接口(无契约冲突)
public class Penguin : Bird
{
public void Swim() => Console.WriteLine("企鹅游泳");
}
// 调用方:仅对"会飞的鸟"调用Fly,无替换风险
public class BirdPark
{
public void LetBirdFly(IFlyingBird bird)
{
bird.Fly();
}
}
关键注意 :里氏替换不仅要求语法上的继承,更要求语义上的兼容 ------ 比如父类方法返回IEnumerable<T>,子类不能返回List<T>的子类(语法允许,但语义可能破坏);父类方法不抛异常,子类也不能随意抛异常。
4. 接口隔离原则(Interface Segregation Principle,ISP)
核心定义 :客户端(调用方)不应该依赖它不需要的接口 ,即大而全的胖接口 要拆分为多个小而精的专用接口。
通俗理解 :接口是给调用方用的,只暴露调用方需要的方法,避免 "强迫客户端实现它用不到的方法",是里氏替换原则的补充。
C# 反例 :定义一个IWorker接口,包含Work()、Eat()、Sleep()方法,机器人(Robot)实现该接口,但机器人不需要Eat()和Sleep(),只能空实现这两个方法 ------ 接口被污染,客户端调用时可能误调无用方法。
C# 正例 :将IWorker拆分为三个专用接口,不同的实现类按需实现,不做无用实现。
csharp
// 专用接口1:工作行为
public interface IWorkable
{
void Work();
}
// 专用接口2:进食行为
public interface IEatable
{
void Eat();
}
// 专用接口3:睡眠行为
public interface ISleepable
{
void Sleep();
}
// 人类:实现所有接口(需要工作、进食、睡眠)
public class Human : IWorkable, IEatable, ISleepable
{
public void Work() => Console.WriteLine("人类工作");
public void Eat() => Console.WriteLine("人类吃饭");
public void Sleep() => Console.WriteLine("人类睡觉");
}
// 机器人:仅实现工作接口(无需进食、睡眠)
public class Robot : IWorkable
{
public void Work() => Console.WriteLine("机器人工作");
}
5. 依赖倒置原则(Dependency Inversion Principle,DIP)
核心定义:
- 高层模块(业务逻辑)不应该依赖低层模块(工具 / 实现类),二者都应该依赖**抽象(接口 / 抽象类)**;
- 抽象不应该依赖细节,细节应该依赖抽象。
通俗理解 :面向抽象编程,而非面向具体实现编程 ,是 SOLID 中最核心的原则 ,也是实现前四个原则的关键,能彻底解耦高层和低层模块。
C# 反例 :高层模块OrderService(订单业务)直接依赖低层模块SqlServerRepository(SQLServer 数据层),若后续要替换为MySqlRepository,必须修改OrderService的代码,耦合度极高。
C# 正例 :定义数据层抽象接口IOrderRepository,高层OrderService依赖该接口,低层SqlServerRepository/MySqlRepository实现该接口,替换数据库时只需新增实现类,不改动高层业务。
csharp
// 抽象接口:数据层规范(高层和低层都依赖它)
public interface IOrderRepository
{
void CreateOrder();
}
// 低层实现1:SQLServer数据层(细节依赖抽象)
public class SqlServerRepository : IOrderRepository
{
public void CreateOrder() => Console.WriteLine("SQLServer创建订单");
}
// 低层实现2:MySql数据层(细节依赖抽象,新增扩展)
public class MySqlRepository : IOrderRepository
{
public void CreateOrder() => Console.WriteLine("MySql创建订单");
}
// 高层模块:订单业务(依赖抽象,不依赖具体实现)
public class OrderService
{
// 构造函数注入:解耦具体实现,由外部传入
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
public void DoCreateOrder()
{
// 仅调用抽象方法,无需关心底层是哪种数据库
_repository.CreateOrder();
}
}
// 调用方:组装依赖(实际开发中由IOC容器<Autofac/DI>自动完成)
public class Program
{
static void Main()
{
// 替换为MySqlRepository,只需修改这一行,OrderService无需任何改动
IOrderRepository repo = new SqlServerRepository();
OrderService service = new OrderService(repo);
service.DoCreateOrder();
}
}
C# 落地关键 :依赖注入(DI)+ 控制反转(IOC),C# 原生提供Microsoft.Extensions.DependencyInjection容器,能完美实现抽象的自动注入,彻底解耦依赖。
总结
SOLID 五大原则是一套协同工作的设计准则,并非孤立存在,核心目标都是让 C# 代码更健壮、易维护,关键要点回顾:
- 单一职责:一个类只做一件事,减少修改点;
- 开闭原则:扩展开放、修改关闭,核心是抽象和多态;
- 里氏替换:子类兼容父类契约,无感知替换,保证继承的合理性;
- 接口隔离:接口拆分为专用小接口,避免强迫实现无用方法;
- 依赖倒置:面向抽象编程,高层 / 低层都依赖抽象,是解耦的核心,C# 中通过 DI/IOC 落地。
在 C# 开发中(如ASP.NET Core、WinForm/WPF),SOLID 是设计框架、业务层、数据层的基础,结合设计模式(工厂、策略、依赖注入等)使用,能最大化发挥其价值。