依赖注入(DI)是实现控制反转(IoC)最常用方式,DI容器负责自动创建对象、解析依赖、管理生命周期;.NET内置Microsoft.Extensions.DependencyInjection为轻量标准生产级容器,手写简易容器有助于理解其注册、解析、缓存三大核心机制及生命周期管理原理。

依赖注入(DI)是实现控制反转(IoC)最常用的方式,而 DI 容器就是负责自动创建对象、解析依赖、管理生命周期的核心组件。在 C# 中,.NET 自带的 Microsoft.Extensions.DependencyInjection 是轻量、标准、生产就绪的 IoC 容器;但要真正理解它,最好先“从零手写一个简易容器”——不是为了替代它,而是看清背后逻辑。
什么是控制反转(IoC)?
IoC 的本质是“把对象的创建和依赖关系的绑定,从代码内部转移到外部容器来管理”。以前你可能这样写:
var logger = new FileLogger(); var service = new UserService(logger); // 手动传入依赖
这导致类与具体实现强耦合,难测试、难替换。IoC 后,你只声明“我需要一个 ILogger”,容器负责给你合适的实例。
手写一个极简 DI 容器(核心三步)
下面是一个仅支持构造函数注入 + 单例/瞬时生命周期的 50 行容器原型,帮你抓住关键脉络:
- 注册(Register):告诉容器「某个接口对应哪个实现类,以及它的生命周期」
- 解析(Resolve):调用时,容器递归分析构造函数参数,自动 new 出完整对象树
- 缓存(Cache):对单例类型只创建一次,后续直接返回缓存实例
// 示例:注册与解析 container.Register(LifeTime.Singleton); container.Register (LifeTime.Transient); var service = container.Resolve
(); // 自动注入 ILogger
关键点在于 Resolve 时用反射读取 UserService 构造函数,发现它需要 ILogger,再查注册表,递归构建——这就是“自动装配”的起点。
.NET 内置容器怎么用?实际项目四步走
生产环境直接用 Microsoft.Extensions.DependencyInjection,它已高度优化且与 ASP.NET Core 深度集成:
- 安装 NuGet 包:
Microsoft.Extensions.DependencyInjection - 创建容器:
var services = new ServiceCollection() - 注册服务(支持三种生命周期):
services.AddSingleton() services.AddScoped() services.AddTransient() - 构建并使用:
var sp = services.BuildServiceProvider();
var logger = sp.GetRequiredService();
注意:Scoped 在 Web 中通常按 HTTP 请求生命周期管理,需搭配 IServiceScope 使用,避免跨作用域访问。
为什么需要生命周期管理?常见陷阱提醒
生命周期不是可选项,而是资源安全的关键:
- Transient:每次 Resolve 都新建实例 → 适合无状态、轻量类(如 DTO 转换器)
- Scoped:同作用域内复用(如一次 Web 请求中所有 Repository 共享同一个 DbContext)→ 避免并发修改或连接泄漏
- Singleton:整个应用生命周期唯一 → 不能持有 Scoped 或 Transient 类型的引用(会延长其生命周期,引发内存泄漏或状态污染)
典型错误:在 Singleton 服务里注入 Scoped 的 DbContext,会导致 DbContext 被长期持有,下次请求拿到的是脏数据或已释放的上下文。
基本上就这些。手写容器帮你拆解原理,.NET 内置容器帮你稳住生产。IoC 不是炫技,而是让类更专注职责、更易替换、更可测试——容器只是让这件事自动化而已。










