企业级C#:为什么银行代码和宠物项目代码是两个不同的世界
\n\n如果你曾经从小型网络工作室跳槽到大型企业或金融科技公司,你就会知道那种轻微的文化冲击感。昨天你还可以在没有测试、只有一个注释//TODO: refactor的情况下提交“热修复”,而今天你收到的代码审查却有20条意见,每个公共方法都需要XML文档,每个依赖项都需要在DI容器中注册。
企业级C#开发不是关于“快速完成”,而是关于“确保三年后代码不会出问题,即使代码作者已经被解雇”。这是关于可预测性、可扩展性和无休止的代码审查。让我们来了解企业级C#开发的实际内容,以及如何避免在入职第一周就被淘汰。
\n\n1. 架构:SOLID不是流行词,而是法则
\n\n在初创公司,你可以容忍一个包含“魔法”字符串和2000行巨型类的单体应用。但在企业级开发中不行。在这里,每个SOLID原则都不是抽象概念,而是行动指南。
\n\n单一职责原则(SRP)
\n你的类不应该同时知道如何发送邮件、保存到数据库和解析XML。如果你在一个类中看到三个或更多的“职责”——这就是代码坏味道。在企业级开发中,这样的代码会在代码审查中被退回,并附上“违反SRP,请重写”的说明。错误代码示例:
\n\npublic 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\npublic 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\n2. 依赖注入——你最好的朋友
\n\n在企业级C#项目中,你几乎永远不会在类内部看到new MyService()。取而代之的是DI容器(通常是Autofac或内置的Microsoft.Extensions.DependencyInjection)。
规则很简单:如果一个类自己创建依赖项,它就违反了依赖倒置原则。在企业级开发中,这被认为是架构错误。示例:
\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在启动类中注册:
\nservices.AddScoped<IExporter, PdfExporter>();\nservices.AddScoped<ReportGenerator>();\n\n为什么需要这样做?首先,测试:你可以模拟IExporter,而不需要生