依赖注入是控制反转(IOC)思想的一种具体实现方式,核心是将对象的依赖关系交由外部容器管理,而不是在对象内部自行创建依赖实例,这样可以让代码各部分之间的耦合度更低,后续修改和测试都更方便。

依赖注入的核心概念
在没有使用依赖注入的场景下,如果一个类需要使用另一个类的功能,通常会在内部直接实例化依赖的对象,比如下面的代码:
// 定义日志接口
public interface ILogService
{
void WriteLog(string message);
}
// 实现日志接口的具体类
public class FileLogService : ILogService
{
public void WriteLog(string message)
{
Console.WriteLine($"写入文件日志:{message}");
}
}
// 业务类,内部直接实例化日志服务
public class UserService
{
private readonly ILogService _logService;
public UserService()
{
// 直接在内部创建依赖实例,耦合度高
_logService = new FileLogService();
}
public void AddUser(string userName)
{
_logService.WriteLog($"添加用户:{userName}");
}
}
这种写法的问题在于,如果后续要把日志实现换成数据库日志,就需要修改UserService的构造函数,不符合开闭原则。而依赖注入就是把依赖的创建放到外部,由容器来注入到需要的类中。
C#中常见的依赖注入方式
构造函数注入
这是C#中最推荐、最常用的依赖注入方式,通过类的构造函数传入依赖的实例,代码示例如下:
public class UserService
{
private readonly ILogService _logService;
// 通过构造函数接收依赖,依赖由外部传入
public UserService(ILogService logService)
{
_logService = logService;
}
public void AddUser(string userName)
{
_logService.WriteLog($"添加用户:{userName}");
}
}
构造函数注入的优势是依赖关系明确,而且可以通过构造函数的参数强制要求依赖必须传入,避免空引用的问题。
属性注入
属性注入是通过公共属性来接收依赖实例,这种方式不是强制要求传入依赖,适合可选的依赖场景,示例如下:
public class UserService
{
// 定义公共属性接收依赖
public ILogService LogService { get; set; }
public void AddUser(string userName)
{
// 使用时需要判断是否为空
LogService?.WriteLog($"添加用户:{userName}");
}
}
ASP.NET Core中的依赖注入使用
ASP.NET Core框架内置了依赖注入容器,不需要额外引入第三方库就可以使用,核心步骤是注册服务和注入使用。
服务注册
在Program.cs文件中,通过IServiceCollection来注册服务,需要指定服务的类型和实现类型,以及服务的生命周期:
var builder = WebApplication.CreateBuilder(args); // 注册服务,指定生命周期为瞬时(每次请求创建新实例) builder.Services.AddTransient<ILogService, FileLogService>(); // 注册业务服务,依赖ILogService会自动注入 builder.Services.AddTransient<UserService>(); var app = builder.Build();
服务生命周期说明
ASP.NET Core的依赖注入容器支持三种服务生命周期,具体区别如下:
| 生命周期类型 | 说明 |
|---|---|
| Transient(瞬时) | 每次请求服务时都会创建一个新的实例,适合轻量级、无状态的服务 |
| Scoped(作用域) | 在同一个请求作用域内,只会创建一个实例,不同请求之间实例不同,适合和请求相关的服务 |
| Singleton(单例) | 整个应用程序生命周期内只会创建一个实例,所有请求共享同一个实例,适合全局通用的服务 |
在控制器中注入使用
注册完成后,就可以在控制器或者其他服务中通过构造函数注入使用注册的服务:
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly UserService _userService;
// 构造函数注入UserService,UserService依赖的ILogService会自动被容器注入
public UserController(UserService userService)
{
_userService = userService;
}
[HttpPost("add")]
public IActionResult AddUser(string userName)
{
_userService.AddUser(userName);
return Ok("用户添加成功");
}
}
依赖注入的优势总结
- 降低代码耦合度,依赖的修改不需要改动使用依赖的类
- 提升代码的可测试性,测试时可以很方便地注入模拟的依赖实例
- 让代码的依赖关系更清晰,便于后续维护和扩展
- 框架内置容器减少了额外依赖,使用成本低
Dependency_InjectionIOC构造函数注入服务生命周期修改时间:2026-06-28 17:33:18