依赖注入设计模式速查手册
2026/6/4 15:22:06 网站建设 项目流程

一、核心概念

术语说明
依赖注入(DI)由外部负责创建和注入对象所需的依赖,而非对象自己new依赖实例
控制反转(IoC)对象创建和生命周期管理的控制权从调用方反转到外部,DI 是 IoC 的一种实现方式
服务(Service)被注入的依赖对象,通常通过接口抽象暴露能力
容器(Container)负责管理服务注册、解析和生命周期的中间件(如IServiceProvider
构造函数注入通过构造函数参数传入依赖,最常用、最推荐的注入方式
属性注入通过公共 setter 属性注入依赖,适用于可选依赖
方法注入通过方法参数注入依赖,适用于一次性使用的场景
生命周期(Lifetime)服务实例的存活时间:Transient(瞬态)、Scoped(作用域)、Singleton(单例)

二、常用操作

常用方式速查

方式关键代码适用场景
构造函数注入public Cls(IDep dep) { _dep = dep; }必需依赖,最推荐
属性注入public IDep Dep { get; set; }可选依赖
手动注册服务services.AddTransient<IFoo, Foo>().NET 内置容器
手动解析服务serviceProvider.GetRequiredService<IFoo>()从容器获取实例

1. 构造函数注入(最常用)

// 定义服务接口 public interface ICustomerRepository { List<Customer> GetList(); bool Add(Customer model); } ​ // 实现服务 public class CustomerRepository : ICustomerRepository { public List<Customer> GetList() => new List<Customer>(); public bool Add(Customer model) => true; } ​ // 通过构造函数注入依赖 —— ViewModel 不再自己 new Repository public class CustomerViewModel { private readonly ICustomerRepository _repository; ​ public CustomerViewModel(ICustomerRepository repository) { _repository = repository; // 外部注入,解耦 } ​ public void LoadData() { var customers = _repository.GetList(); } }

2. 在 MVVM 项目中的实际应用

// 银行系统中的 LoginViewModel —— 通过构造函数注入 Repository 和 Window public class LoginViewModel : ViewModelBase { private readonly UserInfoRepository repository; private readonly Window loginWindow; ​ // 构造函数注入:依赖从外部传入,ViewModel 不关心具体创建方式 public LoginViewModel(UserInfoRepository repository, Window loginWindow) { this.repository = repository; this.loginWindow = loginWindow; } ​ public RelayCommand LoginCommand { get => new RelayCommand((obj) => { // 数据校验 if (string.IsNullOrWhiteSpace(Account)) { MessageBox.Show("账号不能为空!"); return; } ​ if (string.IsNullOrWhiteSpace(Password)) { MessageBox.Show("密码不能为空!"); return; } ​ // 登录逻辑 var exp = Expressionable.Create<ViewUserInfo>(); exp.And(it => it.Account == Account); exp.And(it => it.Password == Password); exp.And(it => it.Status == 0); var list = repository.GetList(exp); ​ if (list.Count > 1) { MessageBox.Show("账号重复!"); return; } else if (list.Count < 1) { MessageBox.Show("账号或密码有误!"); return; } else { LoginInfo.CurrentUser = list[0]; loginWindow.DialogResult = true; } }); } } ​ // 使用时手动注入依赖(传统 WPF 项目无 DI 容器的写法) var repository = new UserInfoRepository(); var viewModel = new LoginViewModel(repository, this); this.DataContext = viewModel;

3. 常见注入方式对比

注入方式适用场景优点缺点
构造函数注入必需依赖强制依赖明确、支持不可变字段构造函数参数过多时臃肿
属性注入可选依赖灵活、不强制依赖可能为 null,运行时才发现缺失
方法注入一次性使用作用域明确不适合长期持有的依赖

4. 服务生命周期

生命周期说明典型场景
Transient(瞬态)每次请求创建新实例无状态服务、轻量级操作
Scoped(作用域)同一作用域内共享实例数据库 DbContext、请求级缓存
Singleton(单例)全局唯一实例配置服务、日志服务、缓存服务
// .NET 内置 DI 容器注册示例 public void ConfigureServices(IServiceCollection services) { // 瞬态:每次注入都是新实例 services.AddTransient<ICustomerRepository, CustomerRepository>(); ​ // 作用域:同一次请求内共享 services.AddScoped<AppDbContext>(); ​ // 单例:全局唯一 services.AddSingleton<ILogService, LogService>(); }

5. 手动实现简易 DI 容器

// 不依赖第三方框架,手写一个简易的服务容器 public class SimpleContainer { private readonly Dictionary<Type, Func<object>> _registrations = new(); ​ // 注册服务:接口 -> 实现工厂 public void Register<TInterface, TImplementation>() where TImplementation : TInterface, new() { _registrations[typeof(TInterface)] = () => new TImplementation(); } ​ // 注册单例 public void RegisterSingleton<TInterface, TImplementation>() where TImplementation : TInterface, new() { var instance = default(TImplementation); _registrations[typeof(TInterface)] = () => { instance ??= new TImplementation(); return instance; }; } ​ // 解析服务 public T Resolve<T>() { if (_registrations.TryGetValue(typeof(T), out var factory)) return (T)factory(); throw new InvalidOperationException($"服务 {typeof(T).Name} 未注册"); } } ​ // 使用示例 var container = new SimpleContainer(); container.Register<IUserRepository, UserRepository>(); var repo = container.Resolve<IUserRepository>();

三、问题排查

错误1:循环依赖(Circular Dependency)

  • 现象:构造函数注入时栈溢出或容器抛出异常

  • 原因:A 依赖 B,B 又依赖 A,形成死循环

  • 解决:提取共同依赖到第三个服务 C;或使用属性注入打破循环

// 错误:A 依赖 B,B 依赖 A public class ServiceA { public ServiceA(ServiceB b) { } } public class ServiceB { public ServiceB(ServiceA a) { } } ​ // 正确:提取共同逻辑到 ServiceC public class ServiceC { /* 共同逻辑 */ } public class ServiceA { public ServiceA(ServiceC c) { } } public class ServiceB { public ServiceB(ServiceC c) { } }

错误2:未注册服务

  • 现象InvalidOperationException: No service for type 'XXX' has been registered

  • 原因:容器中未注册该服务类型

  • 解决:在ConfigureServices中添加services.AddTransient/Scoped/Singleton

错误3:生命周期不匹配

  • 现象:Scoped 服务被 Singleton 依赖时抛出异常或数据混乱

  • 原因:Singleton 生命周期长于 Scoped,导致捕获过期的 Scoped 实例

  • 解决:改用IServiceScopeFactory在需要时创建新作用域

public class SingletonService { private readonly IServiceScopeFactory _scopeFactory; ​ public SingletonService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } ​ public void DoWork() { using var scope = _scopeFactory.CreateScope(); var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>(); // 安全使用 scoped 服务 } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询