C# 依赖注入(Dependency Injection):完整指南

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

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

开始课程

C#中的依赖注入是什么,为什么你需要它



在现代C#和.NET开发中,关键的设计原则之一是依赖注入(DI)。这种模式允许你创建松耦合、可测试且易于维护的代码。类不是独立创建它所需对象的实例(紧耦合),而是从外部注入依赖项。



DI的主要目标是实现控制反转(IoC)原则,其中对象创建的控制权被转移到外部容器或框架。这从根本上改变了应用程序架构,使其灵活且易于变更。



紧耦合的问题



考虑一个没有DI的典型示例:



public class OrderService{    private readonly EmailSender _emailSender;

public OrderService() { // 硬依赖——创建具体类的实例 _emailSender = new EmailSender(); }

public void ProcessOrder(Order order) { // 订单处理逻辑 _emailSender.Send("Order processed"); }}


在这里,OrderService直接依赖于EmailSender。如果我们想将电子邮件发送替换为短信或日志记录,我们将不得不修改OrderService代码。这违反了开闭原则,并使单元测试几乎不可能进行。



在C#中实现依赖注入的主要方式



在C#中,有三种主要方式来实现DI。每种方式都有其自己的用例和特性。



1. 构造函数注入



这是.NET中最流行且推荐的方法。依赖项通过构造函数参数传递,通常存储在私有只读字段(readonly)中。



public interface INotificationService{    void Send(string message);}

public class EmailService : INotificationService{ public void Send(string message) { Console.WriteLine($"Email sent: {message}"); }}

public class OrderService{ private readonly INotificationService _notificationService;

// 依赖项通过构造函数注入 public OrderService(INotificationService notificationService) { _notificationService = notificationService; }

public void ProcessOrder(Order order) { // 逻辑... _notificationService.Send("Order processed"); }}


优点:类需要哪些依赖项清晰可见;依赖项在对象创建后是不可变的;通过替换模拟对象易于测试。



2. 属性注入



依赖项通过公共属性(setter)设置。当依赖项不是类运行所必需的时,较少使用此方法。



public class OrderService{    public INotificationService NotificationService { get; set; }

public OrderService() { }

public void ProcessOrder(Order order) { if (NotificationService != null) { NotificationService.Send("Order processed"); } }}


缺点:你可能会忘记设置依赖项,这将在运行时导致NullReferenceException。建议仅对可选依赖项使用此方法。



3. 方法注入



依赖项直接传递到使用它的方法中。当依赖项仅用于特定操作时,这很方便。



public class OrderService{    public void ProcessOrder(Order order, INotificationService notificationService)    {        // 逻辑...        notification                

博客

书籍推荐