结合你之前关注的 Solon AOT 编译元数据收集、Agent 配置化安全管控(YAML)以及 Token 经
济学下的代码生成优化背景,C# Source Generator(源生成器)处理 Attribute 的核心逻辑是:
在编译期通过语法树(Syntax Tree)静态扫描 Attribute,提取元数据并生成额外的 C# 代码,从
而将运行时的反射开销转化为编译期的静态调用。这与 Solon AOT 中“注解处理器扫描生成
solon-aot.json”的思路完全一致,但 C# SG 直接生成的是可编译的 .cs 代码,而非 JSON 配置文件。
一、 核心机制:从“运行时反射”到“编译期代码生成”
传统 .NET 框架依赖 System.Reflection 在运行时读取 Attribute,性能损耗大且无法被 AOT 友好支持;Source Generator 则彻底改变了这一流程:
静态扫描:SG 在编译器解析源码阶段介入,通过 SyntaxReceiver 或 Incremental Generator 遍历语法树,精准定位标记了特定 Attribute 的类、方法或属性。
元数据提取:直接从语法节点中提取 Attribute 的参数值(如 [MyConfig("Key", "Value")] 中的 "Key" 和 "Value"),无需加载类型实例。
代码注入:根据提取的元数据,动态生成新的 C# 源代码(如注册逻辑、代理类、序列化辅助代码),这些代码会作为编译的一部分被加入程序集,实现“零反射”执行。
二、 标准实现流程:三步走策略
1. 定义 Marker Attribute
动作:创建一个简单的 Attribute 类,用于标记需要被 SG 处理的元素。
关键约束:该 Attribute 必须存在于独立的类库中,且不能依赖任何 SG 生成的代码,避免循环依赖。
csharp
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class GenerateServiceAttribute : Attribute
{
public string ServiceName { get; }
public GenerateServiceAttribute(string serviceName) => ServiceName = serviceName;
}
2. 实现 Incremental Generator(推荐)
优势:相比旧的 ISourceGenerator,IIncrementalGenerator 支持增量编译,仅在相关文件变更时重新执行,大幅降低编译耗时,适配你对 构建效率 的要求。
核心步骤:
注册 Syntax Provider:筛选所有包含 GenerateServiceAttribute 的类声明。
注册 Transformation:将语法节点转换为语义模型(Semantic Model),获取完整的类型信息。
生成代码:使用 StringBuilder 或 IndentedTextWriter 构建新的 C# 代码字符串。
3. 代码生成示例
输入:
csharp
[GenerateService("OrderService")]
public partial class OrderController { ... }
SG 输出(自动生成的 .g.cs 文件):
csharp
namespace MyGenerated
{
public static class ServiceRegistry
{
public static void RegisterServices(IServiceCollection services)
{
// 静态硬编码注册,无反射开销
services.AddSingleton<IOrderService, OrderController>();
}
}
}
三、 工程化最佳实践与陷阱规避
结合你之前的 Agent 工程化 和 确定性架构经验,落地 SG 时需重点关注以下细节:
Partial Class 强制约束
规则:SG 只能扩展 partial 类。如果目标类未标记为 partial,生成的代码将无法编译。
建议:在 SG 中加入诊断报告(Diagnostic),当检测到非 partial 类被标记时,主动抛出编译警告,引导开发者修正。
增量缓存键(Cache Key)设计
痛点:错误的缓存键会导致 SG 在无关文件修改时全量重跑,或在该更新时不更新。
方案:使用 context.SyntaxProvider.ForAttributeWithMetadataName 自动处理依赖追踪,确保仅当 Attribute 或其修饰的目标代码变更时才触发重新生成。
调试与可视化
工具:启用项目文件中的 <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>,将生成的代码输出到 obj/Debug/netX.X/generated 目录。
价值:方便你像审查 Agent 生成的代码一样,直接查看 SG 输出的最终形态,快速排查生成逻辑错误。
避免复杂逻辑
原则:SG 内部应保持纯函数式逻辑,禁止访问文件系统、网络或随机数。
原因:SG 运行在编译器进程中,任何副作用都可能导致不可复现的编译错误,违背你对 确定性构建 的要求。
四、 与 Solon AOT 的对比启示
表格
维度 C# Source Generator Solon AOT (Java)
产出物 .cs 源代码(参与后续编译) JSON 元数据(供 GraalVM 使用)
执行时机 Roslyn 编译阶段 Java 编译后 / Native 构建前
灵活性 高(可生成任意 C# 逻辑) 中(主要用于配置反射/代理)
调试难度 中(需查看生成文件) 高(需分析 Native 镜像构建日志)
这套机制完美契合你对 高性能、低开销、确定性 的工程追求,是将“动态配置”转化为“静态代码”的终极手段。