当前位置: 首页 > news >正文

IOC 容器 H.Iocable

IOC 容器H.Iocable

位置:Source/Providers/H.Iocable

核心文件:

文件作用
Ioc.cs全局 IOC 容器入口。
IocExtension.cs允许在 XAML 中通过 MarkupExtension 获取服务。
IocBindable.cs可绑定的 IOC 对象。
IocThrowIfNone.cs服务不存在时的异常处理。

Ioc本质是对Microsoft.Extensions.DependencyInjection的封装。典型流程:

ServiceCollectionsc=newServiceCollection();ConfigureServices(sc);Ioc.Build(sc);

获取服务:

varservice=Ioc.GetService<IMyService>();

常见注册方式:

services.AddOptions();services.TryAdd(ServiceDescriptor.Singleton<IMyService,MyService>());services.Configure(newAction<MyOptions>(setupAction));

学习重点:

  • AddXXX通常表示注册服务。
  • UseXXX通常表示启用配置或加入设置系统。
  • TryAdd避免覆盖用户自定义服务。
  • Singleton表示全局单例。
  • Options用于模块配置。

H.Iocable IOC 容器详解

一、什么是 IOC

IOC(Inversion of Control,控制反转)是一种设计模式,核心思想是:

将对象的创建和依赖管理交给容器,而不是由代码直接控制。

简单来说,就是对象不再自己创建依赖,而是由容器注入

二、H.Iocable 核心组件

2.1 组件架构

┌─────────────────────────────────────────────────────────────┐ │ H.Iocable │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Ioc.cs │ │ IocExtension │ │ │ │ 全局容器入口 │ │ XAML 绑定扩展 │ │ │ └────────┬────────┘ └─────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ IocBindable │ │ Ioc<T>基类 │ │ │ │ 可绑定的IOC对象 │ │ 泛型单例封装 │ │ │ └─────────────────┘ └─────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Microsoft.Extensions.DependencyInjection │ │ (.NET 原生 DI 框架) │ └─────────────────────────────────────────────────────────────┘

2.2 文件职责说明

文件核心作用使用场景
Ioc.cs全局 IOC 容器入口,提供服务获取和管理C# 代码中获取服务
IocExtension.csXAML 中通过标记扩展获取服务XAML 绑定服务
IocBindable.cs可绑定的 IOC 对象,实现 INotifyPropertyChangedMVVM 绑定场景
Ioc<T>泛型单例封装基类简化单例服务访问

三、核心实现剖析

3.1 Ioc.cs - 全局容器入口

这是整个 IOC 系统的核心,让我们一步步拆解:

publicstaticclassIoc{// 核心字段:服务提供者和服务集合privatestaticIServiceProvider_services=null;privatestaticIServiceCollection_serviceCollection=null;publicstaticIServiceProviderServices=>_services;// 构建容器:将 ServiceCollection 转换为 ServiceProviderpublicstaticvoidBuild(IServiceCollectionserviceCollection){_services=serviceCollection.BuildServiceProvider();_serviceCollection=serviceCollection;}// 获取服务的两种方式publicstaticTGetService<T>(boolthrowIfNone=true){// 容器未初始化时的处理if(_services==null)returnthrowIfNone?thrownewArgumentNullException(...):default;returnGetService<T>(typeof(T),throwIfNone);}publicstaticTGetService<T>(Typetype,boolthrowIfNone=true){Tr=(T)_services.GetService(type);// 服务不存在时的异常处理if(r==null&&throwIfNone)thrownewArgumentNullException(typeof(T).FullName,$"此接口为依赖注入接口,请先在ApplicationBase中注册<{typeof(T).Name}>服务");returnr;}// 检查服务是否存在publicstaticboolExist<T>(){returnGetService<T>(throwIfNone:false)!=null;}// 批量获取实现某个接口的所有服务publicstaticIEnumerable<T>GetAssignableFromServices<T>(Func<T,bool>predicate=null){foreach(ServiceDescriptoritemin_serviceCollection){if(typeof(T).IsAssignableFrom(item.ServiceType)){// 处理实例和工厂两种注册方式if(item.ImplementationInstance==null){// 从容器中获取实例foreach(objectinstanceinServices.GetServices(item.ServiceType)){if(instanceisTit&&predicate?.Invoke(it)!=false)yieldreturnit;}}else{// 直接使用已注册的实例if(item.ImplementationInstanceisTt&&predicate?.Invoke(t)!=false)yieldreturn(T)item.ImplementationInstance;}}}}}

3.2 IocExtension.cs - XAML 中使用 IOC

publicclassIocExtension:MarkupExtension{publicTypeType{get;set;}publicoverrideobjectProvideValue(IServiceProviderserviceProvider){// 在 XAML 解析时获取服务returnIoc.GetService<object>(this.Type);}}

3.3 IocBindable.cs - 可绑定的 IOC 对象

publicabstractclassIocBindable<T,Interface>:Ioc<T,Interface>,INotifyPropertyChangedwhereT:class,Interface,new(){publiceventPropertyChangedEventHandlerPropertyChanged;publicvirtualvoidRaisePropertyChanged([CallerMemberName]stringpropertyName=""){PropertyChanged?.Invoke(this,newPropertyChangedEventArgs(propertyName));}}

3.4 Ioc 泛型基类

// 带实现类型的泛型基类publicabstractclassIoc<T,Interface>whereT:class,Interface{publicstaticTInstance=>Ioc.GetService<Interface>()asT;}// 简化版泛型基类publicabstractclassIoc<Interface>{publicstaticInterfaceInstance=>Ioc.GetService<Interface>(false);}

四、完整使用流程

4.1 第一步:注册服务

App.xaml.csConfigureServices方法中注册:

publicpartialclassApp:ApplicationBase{protectedoverridevoidConfigureServices(IServiceCollectionservices){// 1. 注册选项服务services.AddOptions();// 2. 注册单例服务(推荐使用 TryAdd 避免覆盖)services.TryAdd(ServiceDescriptor.Singleton<IMyService,MyService>());// 3. 注册配置选项services.Configure<MyOptions>(options=>{options.Name="默认名称";options.MaxCount=100;});// 4. 使用扩展方法注册(框架推荐方式)services.AddHome<ProjectHomeViewPresenter>();services.AddProject<AIDIProjectService>();}}

4.2 第二步:容器构建

ApplicationBase会自动完成这一步:

protectedvoidInitServiceCollection(){ServiceCollectionsc=newServiceCollection();this.ConfigureServices(sc);// 调用子类的注册Ioc.Build(sc);// 构建容器// 容器构建后即可使用Ioc.GetService<ILoadGlobalizationOptionsService>(false)?.Load(outstringmessage);}

4.3 第三步:获取服务

方式一:直接获取
// 获取服务(服务不存在时抛异常)varlogService=Ioc.GetService<ILogService>();// 安全获取(服务不存在时返回 null)varoptionalService=Ioc.GetService<IMyOptionalService>(throwIfNone:false);if(optionalService!=null){optionalService.DoSomething();}
方式二:批量获取
// 获取所有实现了 ISplashLoadable 接口的服务IEnumerable<ISplashLoadable>loads=Ioc.GetAssignableFromServices<ISplashLoadable>();foreach(varloadinloads){load.Load(outstringmessage);}
方式三:使用泛型基类
// 定义服务访问类publicclassIocLogService:Ioc<ILogService>{}// 使用时直接访问 Instance 属性IocLogService.Instance.Info("系统启动");
方式四:XAML 中使用
<Windowx:Class="MyApp.MainWindow"xmlns:local="clr-namespace:H.Iocable"><!-- 通过 IocExtension 获取服务并绑定 --><ContentControlContent="{local:IocExtension Type={x:Type local:IMyPresenter}}"/></Window>

五、服务注册方式详解

5.1 生命周期对比

生命周期说明使用场景
Singleton全局唯一实例日志服务、配置服务、全局状态管理
Scoped每个作用域一个实例数据库上下文、事务处理
Transient每次获取新实例轻量级服务、无状态服务

5.2 注册方法对比

// 基础注册(会覆盖已注册的服务)services.AddSingleton<IMyService,MyService>();services.AddScoped<IMyService,MyService>();services.AddTransient<IMyService,MyService>();// 安全注册(不会覆盖已注册的服务)services.TryAdd(ServiceDescriptor.Singleton<IMyService,MyService>());// 实例注册(直接注册已创建的实例)services.AddSingleton<IMyService>(newMyService());// 工厂注册(延迟创建)services.AddSingleton<IMyService>(sp=>{vardependency=sp.GetService<IDependency>();returnnewMyService(dependency);});

5.3 扩展方法注册模式

框架推荐使用扩展方法封装注册逻辑:

publicstaticclassMyModuleExtension{publicstaticIServiceCollectionAddMyModule(thisIServiceCollectionservices,Action<MyOptions>setupAction=null){// 1. 添加选项支持services.AddOptions();// 2. 注册核心服务services.TryAddSingleton<IMyService,MyService>();// 3. 注册配置选项if(setupAction!=null)services.Configure(setupAction);// 4. 链式返回returnservices;}}// 使用时services.AddMyModule(options=>{options.Name="自定义名称";});

六、实际应用案例

6.1 案例一:日志服务

// 定义接口publicinterfaceILogService{voidInfo(stringmessage);voidError(Exceptionex);}// 实现类publicclassLogService:ILogService{publicvoidInfo(stringmessage)=>Console.WriteLine($"INFO:{message}");publicvoidError(Exceptionex)=>Console.WriteLine($"ERROR:{ex.Message}");}// 注册services.AddSingleton<ILogService,LogService>();// 使用Ioc.GetService<ILogService>().Info("应用启动");

6.2 案例二:配置选项

// 定义配置类publicclassAppOptions{publicstringTitle{get;set;}="My App";publicintMaxItems{get;set;}=10;}// 注册配置services.Configure<AppOptions>(options=>{options.Title="WPF Control App";options.MaxItems=100;});// 获取配置varoptions=Ioc.GetService<IOptions<AppOptions>>().Value;Console.WriteLine(options.Title);

6.3 案例三:批量加载服务

// 定义接口publicinterfaceISplashLoadable{stringName{get;}boolLoad(outstringmessage);}// 多个实现publicclassSettingLoader:ISplashLoadable{...}publicclassThemeLoader:ISplashLoadable{...}publicclassDataLoader:ISplashLoadable{...}// 注册services.AddSingleton<ISplashLoadable,SettingLoader>();services.AddSingleton<ISplashLoadable,ThemeLoader>();services.AddSingleton<ISplashLoadable,DataLoader>();// 批量获取并执行varloaders=Ioc.GetAssignableFromServices<ISplashLoadable>();foreach(varloaderinloaders){loader.Load(outstringmessage);}

七、框架约定与最佳实践

7.1 命名约定

前缀含义示例
AddXXX注册服务AddHome(),AddProject()
UseXXX启用配置UseApplicationOptions()
TryAdd安全注册(不覆盖)TryAddSingleton()
IocXXXIOC 访问类IocLogService,IocMessage

7.2 最佳实践

1. 面向接口编程
// ✅ 推荐:依赖接口publicclassMyPresenter{privatereadonlyILogService_logService;publicMyPresenter(ILogServicelogService){_logService=logService;}}// ❌ 不推荐:依赖具体类publicclassMyPresenter{privatereadonlyLogService_logService=newLogService();}
2. 优先使用 TryAdd
// ✅ 推荐:不会覆盖用户自定义实现services.TryAdd(ServiceDescriptor.Singleton<IMyService,DefaultService>());// ❌ 不推荐:会覆盖已注册的服务services.AddSingleton<IMyService,DefaultService>();
3. 使用扩展方法封装
// ✅ 推荐:封装注册逻辑publicstaticIServiceCollectionAddMyFeature(thisIServiceCollectionservices){services.AddOptions();services.TryAddSingleton<IMyService,MyService>();services.TryAddSingleton<IMyPresenter,MyPresenter>();returnservices;}// 使用services.AddMyFeature();
4. 服务不存在时的优雅处理
// ✅ 推荐:安全获取varservice=Ioc.GetService<IMyOptionalService>(throwIfNone:false);if(service!=null){service.DoSomething();}// ✅ 推荐:检查服务是否存在if(Ioc.Exist<IMyOptionalService>()){Ioc.GetService<IMyOptionalService>().DoSomething();}

八、常见问题解答

Q1:为什么要使用 IOC?

A:IOC 带来以下好处:

  • 解耦:组件之间不直接依赖具体实现
  • 可测试:可以轻松替换为 mock 对象
  • 可维护:服务注册集中管理
  • 可扩展:新增服务只需注册即可

Q2:什么时候用 Singleton/Scoped/Transient?

A

  • Singleton:无状态的全局服务(日志、配置)
  • Scoped:需要在特定上下文内共享的服务(数据库连接)
  • Transient:轻量级、每次使用都需要新实例的服务

Q3:如何在 XAML 中使用服务?

A:使用IocExtension

<Windowxmlns:ioc="clr-namespace:H.Iocable"><ContentControlContent="{ioc:IocExtension Type={x:Type local:IMyPresenter}}"/></Window>

Q4:服务注册顺序重要吗?

A:对于Add方法,后注册的会覆盖先注册的;对于TryAdd方法,只有第一次注册有效。


九、总结

H.Iocable 是对 Microsoft.Extensions.DependencyInjection 的轻量级封装,核心价值在于:

  1. 简化使用:提供静态入口Ioc,无需注入即可获取服务
  2. XAML 支持:通过IocExtension实现 XAML 中的服务绑定
  3. 批量服务获取GetAssignableFromServices<T>支持获取多个实现
  4. 优雅的异常处理:服务不存在时提供清晰的错误信息

掌握 IOC 容器是理解 WPF-Control 框架的关键,它是连接所有模块、服务和控件的核心纽带。

http://www.jsqmd.com/news/863184/

相关文章:

  • QMCDecode终极指南:3步快速解锁QQ音乐加密格式,实现音频自由播放
  • QQ音乐加密音频一键解密:3步让Mac用户重获音乐自由
  • Godot纸牌游戏框架:状态语义化与规则声明式设计
  • 浙江必应推广技术全解析:从流量逻辑到落地服务
  • 安卓App动态签名机制逆向解析:从Java到Native全链路还原
  • 老旧小区门禁改造:业主权益与合规指引
  • 3步部署方案:炉石传说佣兵战记自动化脚本实战指南
  • ViGEmBus:为Windows游戏玩家开启虚拟手柄的魔法之门
  • 线粒体氧化磷酸化的新靶点:S-Gboxin的发现与研究进展
  • 爆破地震波信号处理HHT改进算法及应用【附代码】
  • 基于Java Web的退休人才求职网站设计与开发
  • 某瓜App sign参数逆向解析与Python稳定复现
  • 短信验证码5大常见漏洞与防御实战
  • 盐印相不是滤镜,是光学物理建模!:深度解析Midjourney --sref 与 --style raw 联动实现银盐晶体模拟原理
  • 【国家级少数民族语音工程关键进展】:ElevenLabs新疆话语音SDK深度测评——含ASR对齐误差率、情感韵律还原度、宗教文化敏感词过滤机制
  • 前端依赖注入:解耦组件依赖
  • 猫抓浏览器扩展终极指南:三步快速掌握网页视频下载技巧
  • 应用启动基座 `ApplicationBase`
  • NVIDIA Profile Inspector深度解析:解锁700+显卡隐藏设置的专业指南
  • 罗技鼠标宏压枪脚本:基于Lua的游戏后坐力控制系统架构
  • 国密SM2-SM4-SM3混合加密与滑块行为指纹实战解析
  • Services 服务体系
  • 试制类项目审价深度解析[18号文]
  • 智慧医疗药品胶囊缺陷检测数据集VOC+YOLO格式219张5类别有增强
  • 3个维度重塑开发体验:GitHub中文化插件的效率革命
  • 免费解锁显卡隐藏性能:NVIDIA Profile Inspector终极优化指南
  • HTTP安全头配置陷阱与三层验证修复指南
  • Unity中获取物体尺寸的三种核心方法与适用场景
  • 【信息科学与工程学】信息科学领域工程——第十一篇 数据库基础040 关系代数操作
  • 动态字体反爬破解:服务端代劳模式实战