You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用System.Reflection.Emit实现循环依赖类型生成?

使用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处理未完成类型的特殊逻辑,具体解决步骤如下:

  1. 先初始化所有类型的TypeBuilder实例
    不要边创建边调用CreateType(),先把OIDCIdentityResource、OIDCScopeResource、OIDCResource、OIDCScope四个类的TypeBuilder全部定义好,确保每个待生成类型都有对应的未完成Type对象。

  2. 正确绑定泛型接口的类型参数
    为每个TypeBuilder实现接口时,直接传入其他类型的TypeBuilder实例(无需等待类型创建完成)。CLR允许在类型定义阶段引用未完成的Type对象,只要最终所有约束都能满足。

  3. 按依赖层级顺序调用CreateType()
    严格按照类型依赖的先后顺序生成类型:

    • 先创建OIDCIdentityResource(无其他待生成类型依赖)
    • 再创建OIDCScopeResource(依赖已生成的OIDCIdentityResource)
    • 接着创建OIDCResource(依赖已生成的OIDCIdentityResource和已定义的OIDCScopeResource)
    • 最后创建OIDCScope(依赖前面所有已生成的类型)
  4. 严格匹配泛型约束逻辑
    实现接口时,确保泛型参数的映射完全和手动实现一致,比如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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:35:23