C#中的Clean Code:实践原则与示例
干净代码(Clean Code)不仅仅是一个流行术语,而是专业开发人员的基本技能。在.NET和C#生态系统中,项目通常持续多年并由数十名程序员维护,编写清晰且易于修改的代码变得至关重要。本文将分析适用于C#的Clean Code基本原则,并通过具体示例展示其应用。
为什么干净代码在C#中很重要?
C#是一种强类型、面向对象的语言,拥有丰富的生态系统。然而,如果不遵循规范,其强大功能可能导致混乱。干净代码能减少错误数量,加快新开发人员的入职速度,并降低维护成本。正如Robert Martin所说:“快速编写代码的唯一方法就是把它写好。”
原则1:有意义的命名
变量、方法和类的名称应揭示其意图。不要吝啬字符——糟糕的代码需要更多时间来理解。
糟糕的示例
int d; // 自修改以来的时间List<int> lst = new List<int>();void Proc(int a, int b) { ... }良好的示例(Clean Code)
int elapsedTimeInDays;List<int> customerIds = new List<int>();void CalculateDiscount(decimal basePrice, decimal taxRate) { ... }规则:如果名称需要注释,说明它不好。变量使用camelCase,方法和类使用PascalCase。
原则2:小函数(方法级别的SRP)
每个函数应只做一件事,并且做好。如果方法超过20-30行或嵌套超过两层,这就是重构的信号。
糟糕的示例:瑞士军刀函数
public void ProcessOrder(Order order){ // 验证 if (order == null || order.Items.Count == 0) throw new ArgumentException("无效订单");
// 计算折扣 decimal discount = 0; if (order.Total > 1000) discount = 0.1m; else if (order.Total > 500) discount = 0.05m;
// 保存到数据库 using var db = new AppDbContext(); db.Orders.Add(order); db.SaveChanges();
// 发送邮件 SendEmail(order.CustomerEmail, "订单已收到");}良好的示例:职责分离
public void ProcessOrder(Order order){ Validate(order); var discount = CalculateDiscount(order.Total); ApplyDiscount(order, discount); SaveOrder(order); SendConfirmationEmail(order);}
private void Validate(Order order){ if (order == null || order.Items.Count == 0) throw new ArgumentException("无效订单");}
private decimal CalculateDiscount(decimal total){ if (total > 1000) return 0.1m; if (total > 500) return 0.05m; return 0;}
private void SaveOrder(Order order){ using var db = new AppDbContext(); db.Orders.Add(order); db.SaveChanges();}原则3:避免重复(DRY)
重复代码是万恶之源。如果你复制代码块超过一次,请将其提取到方法或类中。
示例:逻辑重复
// 在用户控制器中if (user.Role == "Admin" || user.Role == "Manager") GrantAccess();
// 在报表控制器中if (user.Role == "Admin" || user.Role == "Manager") ShowReport();重构:封装检查
public static class UserExtensions{ public static bool HasElevatedPermissions(this User user) { return user.Role == "Admin" || user.Role == "Manager"; }}
// 使用if (user.HasElevatedPermissions()) GrantAccess();