c# 如何实现依赖注入容器

.NET 内置依赖注入容器是 IServiceCollection + IServiceProvider,原生支持、无需额外安装,仅提供构造函数注入和 Transient/Scoped/Singleton 生命周期管理。

什么是 .NET 内置的依赖注入容器 .NET 从 Core 开始就内置了轻量级 DI 容器,类型是 IServiceCollection + IServiceProvider,它不是第三方库(比如 Autofac 或 DryIoc),而是框架原生支持的抽象层。你不需要额外安装包(Microsoft.Extensions.DependencyInjectionMicrosoft.NET.Sdk 项目中默认引用)。

它的定位是“够用、安全、与 Host 集成好”,不追求高级特性(如属性注入、装饰器、命名注册)。如果你只需要构造函数注入 + 生命周期管理(Transient / Scoped / Singleton),它完全胜任。

如何注册服务并获取实例 注册靠 IServiceCollection 扩展方法,解析靠 IServiceProviderGetService()GetRequiredService()
  • AddTransient():每次请求都新建实例
  • AddScoped():每个作用域(如一次 HTTP 请求)内复用同一个实例
  • AddSingleton():整个应用生命周期只创建一次
var services = new ServiceCollection();
services.AddTransient();
services.AddScoped();
services.AddSingleton();

var provider = services.BuildServiceProvider();

// 使用 var userService = provider.GetRequiredService();

注意:GetService() 返回 null 表示未注册;GetRequiredService() 抛出 InvalidOperationException(更推荐,避免空引用)。

为什么在 Program.cs 中用 CreateHostBuilder 而不是手动 new ServiceCollection 在 Web 项目(.NET 6+)中,你不该手动构建 IServiceProvider,而应通过 Host.CreateDefaultBuilder()WebApplicationBuilder 注入服务:
var builder = WebApplication.CreateBuilder(args);

// 注册服务(自动使用内置容器) builder.Services.AddTransient(); builder.Services.AddHttpClient();

var app = builder.Build();

原因:

  • 手动 BuildServiceProvider() 会提前触发容器构建,导致中间件、配置、日志等尚未注入
  • Host 会帮你处理作用域生命周期(比如 HttpContext 绑定 Scoped 服务)
  • 第三方扩展(如 EF Core、MediatR)依赖 Host 的服务发现机制

容易踩的坑:循环依赖和泛型注册 循环依赖在编译期不报错,运行时首次解析时抛出 InvalidOperationException: A circular dependency was detected。典型场景是 A 依赖 B,B 又依赖 A(包括间接依赖)。

泛型注册要注意闭合类型 vs 开放泛型:

  • services.AddTransient(typeof(IHandler ✅ 支持开放泛型映射
  • services.AddTransient, OrderCreatedHandler>(); ✅ 显式注册闭合类型
  • services.AddTransient, Handler>() ❌ 编译失败 —— C# 不允许泛型类型参数在泛型方法调用中作为实参直接出现

复杂对象图或需要 AOP/装饰器时,内置容器能力很快见顶。这时候才该考虑引入 Autofac 并替换默认容器 —— 但先确认是不是真需要,别一上来就上重武器。