使用反射动态注入依赖对象_简易依赖注入容器实现原理

星浩君_9602

星浩君_9602

2026-02-20

944人浏览

原创

Activator.CreateInstance 不能直接当 DI 容器用,因为它无法自动解析构造函数依赖、不维护生命周期,也不支持类型映射与泛型闭包处理,需手动构建依赖树且易出错。

使用反射动态注入依赖对象_简易依赖注入容器实现原理

为什么 Activator.CreateInstance 不能直接当 DI 容器用

它能创建实例,但没法自动解析构造函数里的依赖,更不维护生命周期。你传一个 IService 进去,它只会报 MissingMethodException 或 ArgumentException —— 因为没告诉它“这个接口该用哪个实现类来填”。

实操建议:

  • 别在构造函数参数里留抽象类型(如 ILogger)就直接调 Activator.CreateInstance,它不认识注册关系
  • 若真要手动凑,得先查好依赖树,按顺序一个个 new 出来再传进去,容易漏、难维护
  • 真正做注入时,必须自己维护一个映射表:比如 typeof(ILogger) → typeof(ConsoleLogger)

怎么用 TypeInfo 和 ConstructorInfo 扫出依赖链

核心是拿到目标类型的构造函数,再遍历它的参数类型,逐层递归 resolve。.NET Core 里常用 GetConstructors() + GetParameters(),但注意:有多个构造函数时,默认取最长的那个(不是 public 第一个)。

常见错误现象:InvalidOperationException: Unable to resolve service for type 'IRepository' while attempting to activate 'UserService' —— 其实就是某一级参数没在你的字典里注册。

实操建议:

  • 只扫描 public 构造函数,忽略 private 或 internal 的,否则可能意外触发副作用
  • 遇到泛型类型(如 IRepository),检查注册表时要用 IsGenericType 和 GetGenericTypeDefinition() 匹配,不能直接比 typeof
  • 缓存已解析过的类型,避免重复反射开销;ConcurrentDictionary 比 Dictionary 更安全

ServiceCollection 和手写容器在生命周期上的关键差异

手写容器如果只返回 new 出来的对象,那每次 Resolve 都是新实例 —— 等同于 Transient。但你没法自然支持 Scoped(比如 HTTP 请求级单例),因为没上下文绑定机制。

性能影响很明显:没作用域管理的容器,在 Web 场景下可能把 DbContext 多次 new 出来,引发连接泄漏或并发异常。

实操建议:

  • 若坚持手写,至少区分三种注册方式:AddTransient(每次都 new)、AddSingleton(首次 new 后缓存)、AddScoped(需外部传入 scope 对象,比如 AsyncLocal>)
  • 不要在 AddSingleton 里注册含 Transient 依赖的对象,否则会把 transient 对象也“升格”成单例,导致状态污染
  • Dispose 管理必须显式做:缓存的 singleton 实例要收集起来,统一调 IDisposable.Dispose(),否则资源不释放

反射注入时最容易被忽略的泛型闭包问题

比如你注册了 typeof(IRepository) → typeof(EntityFrameworkRepository),但实际 resolve IRepository 时,如果不手动调用 MakeGenericType,就会拿不到正确类型,最终抛 InvalidCastException 或返回 null。

这不是语法错误,是类型系统层面的断连 —— IRepository 和 IRepository 在 CLR 里是完全不同的 Type 对象。

实操建议:

  • 注册开放泛型时,存的是 Type.GetGenericTypeDefinition(),resolve 时用 genericDef.MakeGenericType(arguments) 构造闭合类型
  • 参数类型数组要严格对应:顺序、数量、是否可空(int? 和 int 是不同 Type)
  • 别依赖 GetType().Name 做匹配,它不包含泛型参数信息;必须用 FullName 或直接比 Type 引用

泛型类型匹配和生命周期管理这两块,写错不会当场编译失败,但会在运行时某个边缘请求里突然崩掉,而且堆栈还藏得深。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.02.23

519

5

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.02.23

616

5

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.02.23

1216

5

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.05

791

5

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.05.21

1766

6

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

2025.06.09

5034

18

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

2025.06.10

2334

13

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

2025.06.17

4206

17

golang数组大全
golang数组大全

本专题整合了golang数组相关问题解决方法,想了解更多相关内容,请阅读下面的文章。

2025.06.20

5306

22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习