C# (.NET) 的依赖注入容器:完整指南
依赖注入 (DI) 是现代 C# (.NET) 开发中的关键原则之一,它允许创建松散耦合、可测试且易于维护的应用程序。在 .NET 生态系统中,实现 DI 的主要工具是来自 Microsoft.Extensions.DependencyInjection 包的内置容器。在本文中,我们将详细讨论什么是 DI 容器、如何配置它、存在哪些生命周期以及如何在真实项目中正确使用它。
什么是依赖注入容器?
依赖注入容器(或 IoC 容器)是一个软件组件,它自动管理对象的创建和依赖项的注入。您无需手动使用 new 创建对象,而是在容器中注册类型,容器会负责它们的创建和绑定。
使用 DI 容器的主要优点:
- 减少代码耦合 — 类依赖于抽象(接口),而不是具体实现。
- 简化测试 — 可以轻松地将依赖项替换为 mock 对象。
- 集中管理生命周期 — 您可以控制对象的创建和销毁时机。
- 提高灵活性 — 无需更改使用者代码即可更改实现。
安装和配置 DI 容器
在现代 .NET 项目(ASP.NET Core、控制台、Worker Services)中,DI 容器已内置。要使用它,只需将包添加到项目中:
dotnet add package Microsoft.Extensions.DependencyInjection之后,创建一个 IServiceCollection 实例,并在其中注册所有依赖项。让我们看一个简单的服务注册示例:
using Microsoft.Extensions.DependencyInjection;
// 定义接口及其实现public interface IMessageService{ void Send(string message);}
public class EmailService : IMessageService{ public void Send(string message) { Console.WriteLine($"Email sent: {message}"); }}
// 配置容器var services = new ServiceCollection();services.AddTransient<IMessageService, EmailService>();
// 构建服务提供程序var serviceProvider = services.BuildServiceProvider();
// 获取服务实例var messageService = serviceProvider.GetService<IMessageService>();messageService.Send("Hello from DI!");依赖项的生命周期
DI 容器支持三种主要的对象生命周期管理模式:
- Transient (AddTransient) — 每次请求都会创建一个新实例。适用于无状态的轻量级服务。
- Scoped (AddScoped) — 每个作用域一个实例(例如,在 ASP.NET Core 中每个 HTTP 请求一个实例)。非常适合在单个操作范围内具有状态的服务。
- Singleton (AddSingleton) — 整个应用程序只有一个实例。用于缓存、日志记录器、配置。
使用不同生命周期注册的示例:
services.AddTransient<ITransientService, TransientService>();services.AddScoped<IScopedService, ScopedService>();services.AddSingleton<ISingletonService, SingletonService>();重要提示:如果某个服务依赖于生命周期更短的其他服务,可能会导致错误(例如,使用 Scoped 的 Singleton)。这种情况称为“捕获依赖项”,应避免。
通过构造函数注入依赖项
最常见的注入方式是通过构造函数。容器会自动解析所有依赖