--- title: AutoRegisterExportedCollections 生成器 description: 说明批量注册生成器当前会生成什么、可匹配哪些集合与注册器成员,以及 null-skip 与编译期诊断的边界。 --- # AutoRegisterExportedCollections 生成器 `[AutoRegisterExportedCollections]` 用来把“遍历一组配置并逐项调用 registry 方法”的启动样板收敛成一个生成方法。 它最常见的落点确实是 Godot Inspector 导出的数组,但当前生成器真正依赖的不是 `[Export]` 本身,而是: - 宿主类型被标记了 `[AutoRegisterExportedCollections]` - 某个实例字段或可读实例属性被标记了 `[RegisterExportedCollection(...)]` - 该成员可枚举,且元素类型可在编译期推导 - 目标 registry 成员存在,并能找到兼容的单参数实例方法 ## 当前包关系 - 特性来源:`GFramework.Godot.SourceGenerators.Abstractions.UI` - 生成器实现:`GFramework.Godot.SourceGenerators` - 典型消费者:Godot 启动入口、资源入口节点、配置引导节点 ## 最小用法 ```csharp using System.Collections.Generic; using GFramework.Godot.SourceGenerators.Abstractions.UI; using Godot; public interface IKeyValue { } public interface IRegistry { void Registry(IKeyValue mapping); } public interface IAssetRegistry : IRegistry { } public sealed class TextureConfig : Resource, IKeyValue { } [AutoRegisterExportedCollections] public partial class GameEntryPoint : Node { private IAssetRegistry? _textureRegistry; [Export] [RegisterExportedCollection(nameof(_textureRegistry), nameof(IRegistry.Registry))] private Godot.Collections.Array? _textureConfigs; public override void _Ready() { _textureRegistry ??= ResolveTextureRegistry(); __RegisterExportedCollections_Generated(); } private static IAssetRegistry ResolveTextureRegistry() { throw new NotImplementedException(); } } ``` 当前生成器不会自动调用 `__RegisterExportedCollections_Generated()`。你需要在 registry 成员和集合成员都准备好之后手动调用。 ## 当前会生成什么 对于上面的成员,当前生成器会产出: ```csharp private void __RegisterExportedCollections_Generated() { if (this._textureConfigs is not null && this._textureRegistry is not null) { foreach (var __generatedItem in this._textureConfigs) { this._textureRegistry.Registry(__generatedItem); } } } ``` 最重要的运行时语义只有两条: - 集合成员为 `null` 时,本次注册直接跳过 - registry 成员为 `null` 时,本次注册直接跳过 这里的“跳过”只针对运行时 `null` 情况;配置错误、方法不匹配、元素类型无法推导等问题都会在编译期直接给出诊断,而不是静默吞掉。 ## 当前支持的成员形状 ### 集合成员 `[RegisterExportedCollection]` 可以标在: - 实例字段 - 可读、非索引器的实例属性 它们不必一定带 `[Export]`,但在 Godot 项目里通常会配合 `[Export]` 使用。 ### registry 成员 `registryMemberName` 指向的目标也必须是: - 实例字段,或 - 可读、非索引器的实例属性 静态字段、静态属性、只写属性都不受支持。 ## 当前匹配规则 ### 可枚举集合 集合成员必须实现 `System.Collections.IEnumerable`,并且生成器还要能推导出元素类型。 因此: - `List`、`Godot.Collections.Array` 这类泛型集合可以 - 非泛型 `IEnumerable` / `ArrayList` 这类只能枚举 `object` 的集合不可以 ### 注册方法 当前会查找名称匹配、且满足以下条件的方法: - 实例方法 - 只有一个参数 - 对宿主类型可访问 - 参数类型能接收集合元素类型 查找范围不只限于 registry 具体类型本身,还包括: - 基类 - 直接实现的接口 - 继承链上的接口 所以像下面这种接口继承链是受支持的: ```csharp [RegisterExportedCollection(nameof(_registry), "Registry")] public List? Values { get; } = new(); ``` 只要 `_registry` 的接口链上能找到兼容的 `Registry(...)` 即可。 ### 明确不支持的情况 当前测试明确覆盖了这些边界: - 只显式实现接口方法,未在具体类型上暴露可访问成员 - 注册方法存在,但对宿主类型不可访问 - 集合元素类型无法推导 - registry 成员不存在 - 注册方法名存在但签名不兼容 这些情况都会直接触发编译期诊断。 ## 真实采用路径 一个典型的项目侧入口会这样组织导出集合: - `UiPageConfigs` - `GameSceneConfigs` - `PrefabSceneConfigs` - `TextureConfigs` 这几个 `Array` 成员都通过 `[RegisterExportedCollection(...)]` 声明 registry 目标,并在 `_Ready()` 里调用 `__RegisterExportedCollections_Generated()`。 这个例子说明两件事: 1. 这项能力适合“启动时集中接入一批静态配置”的节点 2. 生成器只负责循环调用,不负责 registry 的获取、生命周期或错误恢复 ## 使用约束 当前最重要的约束有这些: - 宿主类型必须是顶层 `partial class` - 不支持嵌套类 - 生成器不会自动接入 `_Ready()` 或其他生命周期方法 - 宿主类型若已声明 `__RegisterExportedCollections_Generated()`,会触发命名冲突诊断 - 只有当至少一个成员成功通过验证时,才会生成方法 ## 诊断速查 | 诊断 ID | 含义 | | --- | --- | | `GF_Common_Class_001` | 宿主类型不是 `partial class` | | `GF_Common_Class_002` | 已手写 `__RegisterExportedCollections_Generated()`,与生成代码冲突 | | `GF_AutoExport_001` | 不支持嵌套类 | | `GF_AutoExport_002` | 指定的 registry 成员不存在 | | `GF_AutoExport_003` | 找不到兼容且可访问的注册方法 | | `GF_AutoExport_004` | 被标记成员不可枚举 | | `GF_AutoExport_005` | 无法安全推导集合元素类型 | | `GF_AutoExport_006` | 集合成员不是实例可读成员 | | `GF_AutoExport_007` | registry 成员不是实例可读成员 | | `GF_AutoExport_008` | `RegisterExportedCollectionAttribute` 构造参数无效 | ## 何时适合用它 适合: - 启动入口里有多组“集合 -> registry”的重复注册代码 - 每个元素都只需要一次简单的单参数注册 - 你想把“注册到哪个 registry、调用哪个方法”直接挂在成员声明上 不适合: - 注册流程需要排序、过滤、去重或事务式回滚 - 每个元素注册前后还要插入复杂副作用 - 注册规则依赖运行时动态上下文,而不是静态成员绑定 ## 推荐阅读 1. [源码生成器总览](./index.md) 2. [配置系统](../game/config-system.md) 3. [Godot 项目生成器](./godot-project-generator.md) 4. [Godot 模块总览](../godot/index.md)