如何用System.Reflection.Emit实现循环依赖类型生成?
问题描述
我有一个基于System.Reflection.Emit的代码生成框架,现在遇到了棘手场景:给定一组带泛型约束的接口(如IOpenIdConnectIdentityResource系列),手动编写实现类可正常编译,但用TypeBuilder创建这些类并调用CreateType()时会抛出类型加载异常。想知道是否存在可行的实现方式?
接口定义
[EntityInterface(EntityKey = "OpenId Connect Identity Resource")] public interface IOpenIdConnectIdentityResource {} [EntityInterface(EntityKey = "OpenId Connect Scope Resource")] public interface IOpenIdConnectScopeResource<TOpenIdConnectResource, TOpenIdConnectScopeResource, TOpenIdConnectIdentityResource> where TOpenIdConnectScopeResource : DynamicEntity, IOpenIdConnectScopeResource<TOpenIdConnectResource, TOpenIdConnectScopeResource, TOpenIdConnectIdentityResource> where TOpenIdConnectResource : DynamicEntity, IOpenIdConnectResource<TOpenIdConnectScopeResource, TOpenIdConnectResource, TOpenIdConnectIdentityResource> where TOpenIdConnectIdentityResource : DynamicEntity, IOpenIdConnectIdentityResource {} [EntityInterface(EntityKey = "OpenId Connect Resource")] public interface IOpenIdConnectResource<TOpenIdConnectScopeResource, TOpenIdConnectResource, TOpenIdConnectIdentityResource> where TOpenIdConnectScopeResource : DynamicEntity, IOpenIdConnectScopeResource<TOpenIdConnectResource, TOpenIdConnectScopeResource, TOpenIdConnectIdentityResource> where TOpenIdConnectResource : DynamicEntity, IOpenIdConnectResource<TOpenIdConnectScopeResource, TOpenIdConnectResource, TOpenIdConnectIdentityResource> where TOpenIdConnectIdentityResource : DynamicEntity, IOpenIdConnectIdentityResource {} [EntityInterface(EntityKey = "OpenId Connect Scope")] public interface IOpenIdConnectScope<TOpenIdConnectScopeResource, TOpenIdConnectResource, TOpenIdConnectIdentityResource> : IOpenIdConnectResource<TOpenIdConnectScopeResource, TOpenIdConnectResource, TOpenIdConnectIdentityResource> where TOpenIdConnectScopeResource : DynamicEntity, IOpenIdConnectScopeResource<TOpenIdConnectResource, TOpenIdConnectScopeResource, TOpenIdConnectIdentityResource> where TOpenIdConnectResource : DynamicEntity, IOpenIdConnectResource<TOpenIdConnectScopeResource, TOpenIdConnectResource, TOpenIdConnectIdentityResource> where TOpenIdConnectIdentityResource : DynamicEntity, IOpenIdConnectIdentityResource {}
手动实现的可行类
public class OIDCScope : DynamicEntity, IOpenIdConnectScope<OIDCScopeResource,OIDCResource,OIDCIdentityResource> {} public class OIDCScopeResource : OIDCIdentityResource, IOpenIdConnectScopeResource<OIDCResource,OIDCScopeResource,OIDCIdentityResource> {} public class OIDCIdentityResource : DynamicEntity, IOpenIdConnectIdentityResource {} public class OIDCResource : DynamicEntity , IOpenIdConnectResource<OIDCScopeResource, OIDCResource,OIDCIdentityResource> {}
可行实现方式
这类问题核心是循环泛型约束的类型依赖顺序,以及TypeBuilder处理未完成类型的特殊逻辑,具体解决步骤如下:
先初始化所有类型的TypeBuilder实例
不要边创建边调用CreateType(),先把OIDCIdentityResource、OIDCScopeResource、OIDCResource、OIDCScope四个类的TypeBuilder全部定义好,确保每个待生成类型都有对应的未完成Type对象。正确绑定泛型接口的类型参数
为每个TypeBuilder实现接口时,直接传入其他类型的TypeBuilder实例(无需等待类型创建完成)。CLR允许在类型定义阶段引用未完成的Type对象,只要最终所有约束都能满足。按依赖层级顺序调用CreateType()
严格按照类型依赖的先后顺序生成类型:- 先创建
OIDCIdentityResource(无其他待生成类型依赖) - 再创建
OIDCScopeResource(依赖已生成的OIDCIdentityResource) - 接着创建
OIDCResource(依赖已生成的OIDCIdentityResource和已定义的OIDCScopeResource) - 最后创建
OIDCScope(依赖前面所有已生成的类型)
- 先创建
严格匹配泛型约束逻辑
实现接口时,确保泛型参数的映射完全和手动实现一致,比如OIDCScopeResource实现IOpenIdConnectScopeResource<OIDCResource, OIDCScopeResource, OIDCIdentityResource>时,泛型参数对应的TypeBuilder必须严格符合约束条件。
关键代码示例
// 1. 先定义所有TypeBuilder实例 var moduleBuilder = ...; // 你的模块构建器实例 var tbIdentity = moduleBuilder.DefineType("OIDCIdentityResource", TypeAttributes.Public, typeof(DynamicEntity)); tbIdentity.AddInterfaceImplementation(typeof(IOpenIdConnectIdentityResource)); var tbScopeResource = moduleBuilder.DefineType("OIDCScopeResource", TypeAttributes.Public, tbIdentity); var scopeResourceInterface = typeof(IOpenIdConnectScopeResource<,,>).MakeGenericType( typeof(TypeBuilder).MakeGenericType(typeof(OIDCResource)), // 直接引用未完成的TypeBuilder tbScopeResource, tbIdentity ); tbScopeResource.AddInterfaceImplementation(scopeResourceInterface); var tbResource = moduleBuilder.DefineType("OIDCResource", TypeAttributes.Public, typeof(DynamicEntity)); var resourceInterface = typeof(IOpenIdConnectResource<,,>).MakeGenericType( tbScopeResource, tbResource, tbIdentity ); tbResource.AddInterfaceImplementation(resourceInterface); var tbScope = moduleBuilder.DefineType("OIDCScope", TypeAttributes.Public, typeof(DynamicEntity)); var scopeInterface = typeof(IOpenIdConnectScope<,,>).MakeGenericType( tbScopeResource, tbResource, tbIdentity ); tbScope.AddInterfaceImplementation(scopeInterface); // 2. 按依赖顺序生成类型 var identityType = tbIdentity.CreateType(); var scopeResourceType = tbScopeResource.CreateType(); var resourceType = tbResource.CreateType(); var scopeType = tbScope.CreateType();
注意事项
- 不要在类型生成前尝试访问未完成Type的成员,CLR会拦截这类操作并抛出异常。
- 若遇到“类型未完成”的异常,检查是否存在依赖顺序错误,确保被依赖的类型优先调用
CreateType()。 - 泛型接口的参数顺序必须和手动实现完全一致,不能出现参数映射错误。
内容的提问来源于stack exchange,提问作者Poul K. Sørensen

