C#通过泛型实现struct循环引用后DLL加载报错如何解决?
问题原因
C# 中结构体是值类型,CLR 加载类型时必须先完成其所有字段类型的加载,才能进一步计算值类型的内存布局和占用大小:
- 直接定义
struct A { A a; }时编译器会直接报错,因为可以直接识别到递归依赖 - 泛型封装的场景能通过编译,是因为C#编译器在编译阶段仅对开放泛型做语法合法性校验,不会深入校验泛型实例化后的闭环依赖;但CLR加载程序集、解析类型的时候,会遍历所有依赖:
Test1依赖Generic<Test2>-> 加载Generic<Test2>这个闭合泛型必须先加载泛型参数Test2->Test2依赖Generic<Test1>-> 加载Generic<Test1>又必须先加载Test1,形成类型加载的死循环,最终抛出ReflectionTypeLoadException。
测试资源管理器找不到测试的问题和该根因一致:测试工具加载程序集枚举类型时会触发同样的类型加载错误。
修复方案
根据业务场景可以选择以下任意一种方案:
- 最简便的方案:将
Test1或Test2任意一个改成类(class)。引用类型的字段仅存储对象指针,大小固定(32位进程4字节、64位进程8字节),不需要递归加载引用对象的完整布局,直接打破依赖循环。 - 必须保留两个类型为结构体的场景:
- 如果你封装的
Generic<T>仅做标记用途、不需要存储T相关数据,可以把字段替换为非泛型的标记类型,直接断开泛型依赖链 - 确需保留泛型关联的话,可以将其中一个字段的类型改为
object或者接口类型,装箱后的值类型字段大小固定,不会触发递归的类型加载 - unsafe 场景下可以将字段改为对应结构体的指针类型,指针大小固定,也可以解决问题
- 如果你封装的
内容的提问来源于stack exchange,提问作者Luka Mandić
相关产品推荐
相关产品推荐

