当前位置: 首页 > news >正文

C#依赖注入

一、什么是依赖注入?

依赖指的是:当一个类(A)需要另一个类(B)的功能才能完成工作时,A 就 "依赖" 于 B。
例如:OrderService需要用Logger记录日志,那么OrderService依赖于Logger

依赖注入的核心思想是:将依赖的创建和管理交给外部容器,而不是在类内部自行创建。
简单说就是 "谁需要依赖,就由外部把依赖传给它",而非自己创建。
 

二、为什么需要依赖注入?

先看一个 "没有依赖注入" 的反例:
// 日志类
public class Logger
{public void Log(string message) => Console.WriteLine($"日志:{message}");
}// 订单服务(依赖Logger)
public class OrderService
{// 自己创建依赖(问题所在)private readonly Logger _logger = new Logger();public void CreateOrder(){_logger.Log("订单创建成功");// 其他业务逻辑...
    }
}
View Code

这个例子中OrderService直接依赖Logger的具体实现,如果未来要替换为FileLogger,必须修改OrderService,这就违反了开闭原则,对扩展开放,对修改关闭

如果使用依赖注入,应该要生命一个抽象接口,让服务类去依赖接口,以后要添加别的类型的日志的适合直接添加就行,然后在逻辑代码中实例化接口,传入给服务类的构造函数。

// 抽象接口(依赖抽象)
public interface ILogger
{void Log(string message);
}// 具体实现
public class ConsoleLogger : ILogger
{public void Log(string message) => Console.WriteLine($"日志:{message}");
}// 订单服务(依赖通过外部注入)
public class OrderService
{private readonly ILogger _logger;// 构造函数注入(推荐方式)public OrderService(ILogger logger){_logger = logger; // 依赖由外部传入,而非自己创建
    }public void CreateOrder(){_logger.Log("订单创建成功");}
}
View Code

三、依赖注入的 3 种方式

在 C# 中,依赖注入通常通过以下 3 种方式实现:

1. 构造函数注入(最推荐)

通过类的构造函数接收依赖,是.NET 中最常用的方式。适用于 "必须的依赖"(类没有它就无法工作)。
 
public class UserService
{private readonly ILogger _logger;// 构造函数注入:创建UserService时必须提供ILoggerpublic UserService(ILogger logger){_logger = logger ?? throw new ArgumentNullException(nameof(logger));}
}
View Code

2. 属性注入

通过公共属性接收依赖,适用于 "可选的依赖"(类没有它也能工作)。
在ASP.NET Core 中,通常配合[Inject]特性使用(如在视图中)
public class UserService
{// 属性注入:依赖是可选的public ILogger Logger { get; set; }public void DoWork(){Logger?.Log("执行工作"); // 注意判空
    }
}
View Code

3. 方法注入

通过方法参数接收依赖,适用于仅在特定方法中需要的依赖。
public class UserService
{public void DoWork(ILogger logger){logger.Log("执行工作"); // 仅该方法需要依赖
    }
}
View Code

 

四、微软自带的Microsoft.Extensions.DependencyInjection

1.核心原理是通过服务容器管理服务的注册、生命周期和依赖解析,其设计遵循 “依赖倒置” 和 “控制反转” 原则,核心流程可分为服务注册、容器构建和服务解

using Microsoft.Extensions.DependencyInjection;// 定义服务接口和实现
public interface IUserService { }
public class UserService : IUserService { }// 注册服务其实就是一个LIst<ServiceDescriptor>集合
var services = new ServiceCollection();
// 瞬时生命周期,new一个新的元素,参数:抽象类型为接口,具体实现类型,Transient是生命周期
services.AddTransient<IUserService, UserService>();
// 作用域生命周期
services.AddScoped<IUserService, UserService>();
// 单例生命周期
services.AddSingleton<IUserService, UserService>();

// 3. 构建服务提供器(DI容器)
_serviceProvider = services.BuildServiceProvider();

2.容器构建(BuildServiceProvider

当服务注册完成后,调用 services.BuildServiceProvider() 会创建 IServiceProvider 实例(默认实现为 ServiceProvider)。这一步是 “从注册到可用容器” 的关键转换,核心工作包括:

验证服务注册的合法性

为每个服务创建 “解析器”

初始化根容器与作用域管理

3.服务解析(GetService/GetRequiredService

当调用 serviceProvider.GetRequiredService<T>() 时,ServiceProvider 会按以下流程解析服务实例:

查找服务元数据

根据生命周期创建实例

 自动注入依赖(构造函数注入)

 处理 IDisposable 服务

4.最后总结原理就类似 :

1)注册阶段:new一个字典集合,注册:相当于添加元素,添加接口和实现类指定生命周期方式

2)构建阶段:BuildServiceProvider() 验证注册合法性,生成解析器,初始化根容器。

3)解析阶段:

据服务类型查找 ServiceDescriptor。按生命周期(Transient/Scoped/Singleton)创建实例,递归注入依赖。缓存实例(Scoped 缓存于作用域,Singleton 缓存于根容器)。

4)清理阶段:作用域或根容器释放时,自动清理 IDisposable 服务。

http://www.hskmm.com/?act=detail&tid=10666

相关文章:

  • 完整教程:Docker Compose 一键启动多容器服务
  • 【截稿倒计时、高录用、稳检索】2025年教育创新与信息技术国际学术会议(EIIT 2025)
  • 低代码 + AI 构建智慧校园系统:某高校宿舍报修平台的48小时构建全流程
  • [MCP][07]logging和progress等功能说明
  • 端口命令
  • Microsoft OLE漏洞致远程代码执行安全公告解析
  • 写代码还是写提示词?——Prompt 工程是不是程序员的新技能树
  • c-store发送dcm文件超时
  • 解码C语言模块化编程
  • redis存储漂流瓶信息
  • hashcat高效爆破Wi-Fi密码方法(比aircrack-ng快)
  • 更新到macOS Sequoia后,chrome无法用ip访问192.168.*
  • Typora标题自动显示序号,大纲中也显示序号
  • 【IEEE出版、格林威治大学主办】第六届信息科学与并行、分布式处理国际学术会议(ISPDS 2025)
  • Day18面向对象的基本认识与回顾方法的定义
  • 【2025-09-19】连岳摘抄
  • 【2025-09-18】工作情绪
  • Ubuntu 系统部署 LNMP 环境详细教程(附shell脚本一键部署↓) - 指南
  • 详细介绍:7HTMLCSS高级
  • MAUI和Android、IOS的互操作之IOS篇
  • OBS Studio视频直播录制软件下载安装教程(录屏直播软件下载)
  • 【Java】Hashtable讲解
  • 使用Inno Setup打包安装程序exe流程
  • 攻防世界 web
  • 批判 vs 审判
  • XXL-JOB-源码分享(1)
  • ctfshow web入门 SSRF
  • C#中避免GC压力和提高性能的8种技术
  • ctfshow web入门 爆破
  • 函数内联