C#源生成器中父子对象初始化的动态最大计数设置方案
解决方案:动态按需创建Child实例,替代预初始化逻辑
核心问题分析
原来的CsharpTypeBase构造函数会递归预创建最多5个子实例,导致两个明显问题:
- 不需要多层嵌套时,多余的Child实例会浪费内存
- 嵌套层数超过5时,
output.Child!的强制解引用会触发空引用异常
解决思路是移除预创建逻辑,改为在实际需要处理子类型时才动态创建Child实例,同时支持任意深度的嵌套场景。
重构后的CsharpTypeBase类
首先修改基类,彻底去掉构造函数里的自动创建Child逻辑,新增按需创建的方法:
public class CsharpTypeBase { public CsharpTypeBase() { } public bool IsSimple { get; set; } public bool IsArray { get; set; } public bool IsIEnumerable { get; set; } public CsharpTypeBase? Child { get; private set; } // 新增方法:确保Child实例存在,不存在则创建 public CsharpTypeBase EnsureChild() { return Child ??= new CsharpTypeBase(); } public string? ValueIfIsSimple { get => _valueIfIsSimple; set { IsSimple = true; _valueIfIsSimple = value; } } public CsharpClassModel ClassModel { get => _classModel; set { IsSimple = false; _classModel = value; } } private string? _valueIfIsSimple; private CsharpClassModel _classModel = new(); }
关键改动说明
- 移除构造函数的
childCount参数和递归创建Child的逻辑,避免预分配不必要的实例 - 将
Child的ref属性改为普通可空属性,符合常规使用逻辑(若有特殊性能需求可保留ref,但按需创建逻辑不变) - 添加
EnsureChild()方法,通过空合并运算符实现“存在则返回,不存在则创建”的逻辑
重构后的类型解析方法
修改FromTypeSyntax方法,在需要处理子类型时,通过EnsureChild()获取或创建Child实例,不再依赖预创建对象:
public static CsharpTypeBase Convert(TypeSyntax typeSyntax) { var output = new CsharpTypeBase(); FromTypeSyntax(typeSyntax, output); return output; } private static void FromTypeSyntax(TypeSyntax typeSyntax, CsharpTypeBase output) { switch (typeSyntax) { case PredefinedTypeSyntax predefinedTypeSyntax: output.ValueIfIsSimple = predefinedTypeSyntax.Keyword.ValueText; output.IsIEnumerable = false; output.IsArray = false; break; case IdentifierNameSyntax identifierNameSyntax: CsharpClassModel.RecursivelyWalkThroughTheClass(identifierNameSyntax.Identifier.ValueText, output); break; case ArrayTypeSyntax arrayTypeSyntax: output.IsArray = true; output.IsIEnumerable = false; // 确保Child存在后再递归处理元素类型 FromTypeSyntax(arrayTypeSyntax.ElementType, output.EnsureChild()); break; case GenericNameSyntax genericNameSyntax: var (innerClass, isEnumerable) = FindTheMostInnerType(genericNameSyntax); output.IsIEnumerable = isEnumerable; // 确保Child存在后再递归处理内部类型 FromTypeSyntax(innerClass, output.EnsureChild()); break; } }
关键改动说明
- 将
FromTypeSyntax的第二个参数从ref CsharpTypeBase改为CsharpTypeBase,因为现在不需要修改引用本身,只需修改对象属性 - 在处理数组和泛型类型时,调用
output.EnsureChild()确保Child实例非空,避免空引用异常 - 移除原代码中
ref output.Child!的强制解引用,消除潜在的运行时错误
适配所有嵌套场景的验证
该方案可完美适配你列出的所有嵌套类型:
List<string>[]:外层数组的Child是List,List的Child是string(简单类型)List<string[]> two:外层List的Child是数组,数组的Child是stringTask<List<IReadOnlyCollection<FirstDto>>> three:Task的Child是List,List的Child是IReadOnlyCollection,其Child是FirstDtoList<List<List<List<List<List<string>>>>>> seven:6层List嵌套,每一层都会按需创建Child,无层数限制
额外优化建议
- 若需防止无限嵌套导致栈溢出,可在
EnsureChild()中添加嵌套深度计数,超过预设最大值时抛出异常或终止递归 - 可为
CsharpTypeBase添加int Depth属性,跟踪当前类型的嵌套深度,方便调试和日志输出
内容的提问来源于stack exchange,提问作者AliSalehi
相关产品推荐
相关产品推荐

