C#.NET 依赖注入详解

发布于:2025-07-29 ⋅ 阅读:(13) ⋅ 点赞:(0)

一、是什么  

        在 C#.NET 中,依赖注入(Dependency Injection,简称 DI) 是一种设计模式,用于实现控制反转(Inversion of Control,IoC),以降低代码耦合、提高可测试性和可维护性。

        依赖注入是将一个对象的依赖(即它所需的其他对象或服务)通过外部提供(注入)的方式传递给它,而不是由对象自身创建或查找依赖。

        其核心思想是将对象的创建和依赖管理交给容器(IoC 容器),从而解耦代码。DI 是现代 .NET 开发(尤其是 ASP.NET Core)的核心特性之一,广泛应用于企业级应用。

一个生活化的比喻:想象一下,你想喝一杯咖啡。

❌没有依赖注入的做法

你亲自去买咖啡豆,亲自找来磨豆机,亲自烧水,亲自操作咖啡机,最后做出一杯咖啡。在这个过程中,你(这个对象)强依赖咖啡豆磨豆机咖啡机等具体事物。如果明天你想换个牌子的咖啡豆,或者磨豆机坏了要换新的,你就得亲自去修改整个流程(修改你的代码)。

✔️有依赖注入的做法

你走进一家咖啡店,对服务员说:“来一杯拿铁”。你并不关心咖啡豆是哪个庄园的,用的是什么牌子的咖啡机。服务员( “注入器” )会把所有你需要的东西(依赖项)准备好,然后把一杯完美的拿铁(咖啡这个对象) “注入” 到你手中。

在这个比喻中:

  • :就是我们的 客户端(Client) ,即需要使用其他服务的类。
  • 咖啡:就是 服务(Service) ,即被客户端使用的依赖对象。
  • “来一杯拿铁”这个动作:就是客户端声明它需要一个服务。
  • 服务员:就是 依赖注入容器(DI Container) ,它负责创建和管理服务的实例,并将其提供给客户端。

核心思想:一个类(你)不应该自己动手创建它所依赖的对象(咖啡)。相反,它应该在被创建的时候,由外部(服务员)将这些依赖关系传递(注入)给它。

这种思想,叫做“控制反转”(Inversion of Control, IoC)。而依赖注入(DI)是实现控制反转最常见的一种设计模式。

二、为什么

依赖注入带来的好处

如果我们不使用 DI,代码通常会像这样(紧密耦合):

public class NotificationService
{
    private readonly SmsSender _smsSender;

    public NotificationService()
    {
        // 问题所在:NotificationService 强行依赖了【具体】的 SmsSender 类
        // 它自己负责创建这个依赖项
        _smsSender = new SmsSender(); 
    }

    public void SendNotification(string message)
    {
        _smsSender.Send(message);
    }
}

这段代码有什么问题?

  1. 不灵活:如果明天产品经理说,我们也要支持邮件通知。你就必须修改 NotificationService 的内部代码,加入 EmailSender,甚至可能要添加复杂的 if/else 来决定用哪个。
  2. 难测试:在进行单元测试时,你无法轻易地把 SmsSender 换成一个“假的”发送器(Mock对象)。你测试 NotificationService 的时候,可能会真的发送一条短信出去,这既浪费资源又不是我们想要的结果。

使用了依赖注入后,代码会变得(松散耦合) :

第1步:定义一个“标准”接口

我们不关心具体是短信还是邮件,只关心它有没有一个“发送”的功能。

// 定义一个服务接口(标准)
public interface IMessageSender
{
    void Send(string message);
}

第2步:创建具体的服务实现

// 短信发送器
public class SmsSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"通过短信发送: {message}");
    }
}

// 邮件发送器
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"通过邮件发送: {message}");
    }
}

第3步:改造客户端,让它依赖于“标准”而非“具体”

public class NotificationService
{
    private readonly IMessageSender _sender;

    // 重点:依赖项通过构造函数被【注入】进来
    // NotificationService 不再关心 _sender 到底是短信还是邮件,它只知道这个东西能发消息
    public NotificationService(IMessageSender sender)
    {
        _sender = sender;
    }

    public void SendNotification(string message)
    {
        _sender.Send(message);
    }
}

第4步:主程序调用

依赖注入(DI): 在创建 NotificationService 实例时,我们在构造函数中传入了 SmsSender 的实例,而不是让 NotificationService 自己创建这个实例。这种方式被称为依赖注入(DI)。通过 DI,我们可以轻松地将不同的 IMessageSender 实现(如 EmailSender)注入到 NotificationService 中,从而实现了代码的解耦和灵活性

static void Main(string[] args)
{
    NotificationService service = new NotificationService(new SmsSender());
    //想通过邮件发送只需更改传入的实例 如:
    //NotificationService service = new NotificationService(new EmailSender());

    service.SendNotification("Hello, world!");
    Console.ReadKey();

}

这样做的好处显而易见:

  1. 松散耦合 (Loose Coupling) :NotificationService 只依赖于 IMessageSender 接口,而不再是某个具体的发送类。你可以轻松地换成 EmailSenderPushSender 或任何实现了 IMessageSender 接口的类,而无需修改 NotificationService 的任何代码。
  2. 易于测试 (Increased Testability) :测试时,我们可以轻松地创建一个 MockMessageSender 类,并将其注入到 NotificationService 中,从而可以在不依赖任何外部服务的情况下独立测试其逻辑。
  3. 代码更清晰、更易维护 (Better Code Organization) :职责分离,NotificationService 只负责业务逻辑,而对象的创建和组装则交给了外部的 DI 容器。

网站公告

今日签到

点亮在社区的每一天
去签到