企业级C#:如何编写在代码审查中拿得出手的代码

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

企业级C#:为什么银行代码和宠物项目代码是两个不同的世界

\n\n

如果你曾经从小型网络工作室跳槽到大型企业或金融科技公司,你就会知道那种轻微的文化冲击感。昨天你还可以在没有测试、只有一个注释//TODO: refactor的情况下提交“热修复”,而今天你收到的代码审查却有20条意见,每个公共方法都需要XML文档,每个依赖项都需要在DI容器中注册。

\n\n

企业级C#开发不是关于“快速完成”,而是关于“确保三年后代码不会出问题,即使代码作者已经被解雇”。这是关于可预测性、可扩展性和无休止的代码审查。让我们来了解企业级C#开发的实际内容,以及如何避免在入职第一周就被淘汰。

\n\n

1. 架构:SOLID不是流行词,而是法则

\n\n

在初创公司,你可以容忍一个包含“魔法”字符串和2000行巨型类的单体应用。但在企业级开发中不行。在这里,每个SOLID原则都不是抽象概念,而是行动指南。

\n\n

单一职责原则(SRP)

\n

你的类不应该同时知道如何发送邮件、保存到数据库和解析XML。如果你在一个类中看到三个或更多的“职责”——这就是代码坏味道。在企业级开发中,这样的代码会在代码审查中被退回,并附上“违反SRP,请重写”的说明。错误代码示例:

\n\n
public class OrderService\n{\n    public void ProcessOrder(Order order)\n    {\n        // 1. 验证\n        // 2. 保存到数据库\n        // 3. 发送邮件\n        // 4. 日志记录\n    }\n}
\n\n

正确的方法是将代码拆分为独立的服务,并使用Mediator或Pipeline模式:

\n\n
public class ProcessOrderCommandHandler : IRequestHandler<ProcessOrderCommand, bool>\n{\n    private readonly IOrderRepository _repo;\n    private readonly IEmailService _email;\n    private readonly ILogger<ProcessOrderCommandHandler> _logger;\n\n    public ProcessOrderCommandHandler(IOrderRepository repo, IEmailService email, ILogger<ProcessOrderCommandHandler> logger)\n    {\n        _repo = repo;\n        _email = email;\n        _logger = logger;\n    }\n\n    public async Task<bool> Handle(ProcessOrderCommand request, CancellationToken cancellationToken)\n    {\n        var order = await _repo.GetByIdAsync(request.OrderId);\n        // 业务逻辑\n        await _email.SendAsync(order.CustomerEmail, "Order processed");\n        _logger.LogInformation("Order {Id} processed", order.Id);\n        return true;\n    }\n}
\n\n

请注意:每个服务只做一件事。测试这样的代码要容易得多。

\n\n

2. 依赖注入——你最好的朋友

\n\n

在企业级C#项目中,你几乎永远不会在类内部看到new MyService()。取而代之的是DI容器(通常是Autofac或内置的Microsoft.Extensions.DependencyInjection)。

\n\n

规则很简单:如果一个类自己创建依赖项,它就违反了依赖倒置原则。在企业级开发中,这被认为是架构错误。示例:

\n\n
// ❌ 错误:紧耦合\npublic class ReportGenerator\n{\n    private readonly PdfExporter _exporter = new PdfExporter();\n    public byte[] Generate() => _exporter.Export();\n}\n\n// ✅ 正确:依赖倒置\npublic class ReportGenerator\n{\n    private readonly IExporter _exporter;\n    public ReportGenerator(IExporter exporter) => _exporter = exporter;\n    public byte[] Generate() => _exporter.Export();\n}
\n\n

在启动类中注册:

\n
services.AddScoped<IExporter, PdfExporter>();\nservices.AddScoped<ReportGenerator>();
\n\n

为什么需要这样做?首先,测试:你可以模拟IExporter,而不需要生

博客

书籍推荐