# GFramework.SourceGenerators > 编译时代码生成 - 零运行时开销的代码增强工具 GFramework.SourceGenerators 是 GFramework 框架的源代码生成器包,通过编译时分析自动生成样板代码,显著提升开发效率并减少运行时开销。 ## 📋 目录 - [概述](#概述) - [核心特性](#核心特性) - [安装配置](#安装配置) - [Log 属性生成器](#log-属性生成器) - [ContextAware 属性生成器](#contextaware-属性生成器) - [GenerateEnumExtensions 属性生成器](#generateenumextensions-属性生成器) - [诊断信息](#诊断信息) - [性能优势](#性能优势) - [使用示例](#使用示例) - [最佳实践](#最佳实践) - [常见问题](#常见问题) ## 概述 GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译时分析你的代码并自动生成常用的样板代码,让开发者专注于业务逻辑而不是重复的模板代码。 ### 核心设计理念 - **零运行时开销**:代码在编译时生成,无反射或动态调用 - **类型安全**:编译时类型检查,避免运行时错误 - **开发效率**:自动生成样板代码,减少重复工作 - **可配置性**:支持多种配置选项满足不同需求 ## 核心特性 ### 🎯 主要生成器 - **[Log] 属性**:自动生成 ILogger 字段和日志方法 - **[ContextAware] 属性**:自动实现 IContextAware 接口 - **[GenerateEnumExtensions] 属性**:自动生成枚举扩展方法 ### 🔧 高级特性 - **智能诊断**:生成器包含详细的错误诊断信息 - **增量编译**:只生成修改过的代码,提高编译速度 - **命名空间控制**:灵活控制生成代码的命名空间 - **可访问性控制**:支持不同的访问修饰符 ## 安装配置 ### NuGet 包安装 ```xml net6.0 ``` ### 项目文件配置 ```xml net6.0 true Generated ``` ## Log 属性生成器 [Log] 属性自动为标记的类生成日志记录功能,包括 ILogger 字段和便捷的日志方法。 ### 基础使用 ```csharp using GFramework.SourceGenerators.Attributes; [Log] public partial class PlayerController { public void DoSomething() { Logger.Info("Doing something"); // 自动生成的 Logger 字段 Logger.Debug("Debug information"); Logger.Warning("Warning message"); Logger.Error("Error occurred"); } } ``` ### 生成的代码 编译器会自动生成如下代码: ```csharp // using Microsoft.Extensions.Logging; namespace YourNamespace { public partial class PlayerController { private static readonly ILogger Logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(); } } ``` ### 高级配置 ```csharp [Log( fieldName = "CustomLogger", // 自定义字段名 accessModifier = AccessModifier.Public, // 访问修饰符 isStatic = false, // 是否为静态字段 loggerName = "Custom.PlayerLogger", // 自定义日志器名称 includeLoggerInterface = true // 是否包含 ILogger 接口 )] public partial class CustomLoggerExample { public void LogSomething() { CustomLogger.LogInformation("Custom logger message"); } } ``` ### 配置选项说明 | 参数 | 类型 | 默认值 | 说明 | |--------------------------|----------------|----------|---------------------| | `fieldName` | string | "Logger" | 生成的日志字段名称 | | `accessModifier` | AccessModifier | Private | 字段访问修饰符 | | `isStatic` | bool | true | 是否生成静态字段 | | `loggerName` | string | null | 自定义日志器名称,null 时使用类名 | | `includeLoggerInterface` | bool | false | 是否包含 ILogger 接口实现 | ### 静态类支持 ```csharp [Log] public static partial class MathHelper { public static int Add(int a, int b) { Logger.Debug($"Adding {a} and {b}"); return a + b; } } ``` ### 日志级别控制 ```csharp [Log(minLevel = LogLevel.Warning)] public partial class WarningOnlyLogger { public void ProcessData() { Logger.Debug("This won't be logged"); // 低于最小级别,被过滤 Logger.Warning("This will be logged"); Logger.Error("This will also be logged"); } } ``` ## ContextAware 属性生成器 [ContextAware] 属性自动实现 IContextAware 接口,提供便捷的架构上下文访问能力。 ### 基础使用 ```csharp using GFramework.SourceGenerators.Attributes; using GFramework.Core.Abstractions; [ContextAware] public partial class PlayerController : IController { public void Initialize() { // Context 属性自动生成,提供架构上下文访问 var playerModel = Context.GetModel(); var combatSystem = Context.GetSystem(); Context.SendEvent(new PlayerInitializedEvent()); } } ``` ### 生成的代码 编译器会自动生成如下代码: ```csharp // using GFramework.Core.Abstractions; namespace YourNamespace { public partial class PlayerController : IContextAware { private IContextAware.Context _context; public IContextAware.Context Context => _context ??= new LazyContext(this); public void SetContext(IContextAware.Context context) { _context = context; } public IContextAware.Context GetContext() { return _context; } } } ``` ### 延迟初始化 ```csharp [ContextAware(useLazy = true)] public partial class LazyContextExample { public void AccessContext() { // Context 会延迟初始化,直到第一次访问 var model = Context.GetModel(); } } ``` ### 上下文验证 ```csharp [ContextAware(validateContext = true)] public partial class ValidatedContextExample { public void AccessContext() { // 每次访问都会验证上下文的有效性 var model = Context.GetModel(); if (Context.IsInvalid) { throw new InvalidOperationException("Context is invalid"); } } } ``` ### 与其他属性组合 ```csharp [Log] [ContextAware] public partial class AdvancedController : IController { public void ProcessRequest() { Logger.Info("Processing request"); var model = Context.GetModel(); Logger.Info($"Player health: {model.Health}"); Context.SendCommand(new ProcessCommand()); Logger.Debug("Command sent"); } } ``` ## GenerateEnumExtensions 属性生成器 [GenerateEnumExtensions] 属性为枚举类型生成便捷的扩展方法,提高代码可读性和类型安全性。 ### 基础使用 ```csharp using GFramework.SourceGenerators.Attributes; [GenerateEnumExtensions] public enum GameState { Playing, Paused, GameOver, Menu } // 自动生成的扩展方法: public static class GameStateExtensions { public static bool IsPlaying(this GameState state) => state == GameState.Playing; public static bool IsPaused(this GameState state) => state == GameState.Paused; public static bool IsGameOver(this GameState state) => state == GameState.GameOver; public static bool IsMenu(this GameState state) => state == GameState.Menu; public static bool IsIn(this GameState state, params GameState[] values) { return values.Contains(state); } } // 使用示例 public class GameManager { private GameState _currentState = GameState.Menu; public bool CanProcessInput() { return _currentState.IsPlaying() || _currentState.IsMenu(); } public bool IsGameOver() { return _currentState.IsGameOver(); } public bool IsActiveState() { return _currentState.IsIn(GameState.Playing, GameState.Menu); } } ``` ### 自定义扩展方法 ```csharp [GenerateEnumExtensions( generateIsMethods = true, generateHasMethod = true, generateInMethod = true, customPrefix = "Is", includeToString = true )] public enum PlayerState { Idle, Walking, Running, Jumping, Attacking } // 生成更多扩展方法 public static class PlayerStateExtensions { public static bool IsIdle(this PlayerState state) => state == PlayerState.Idle; public static bool IsWalking(this PlayerState state) => state == PlayerState.Walking; public static bool IsRunning(this PlayerState state) => state == PlayerState.Running; public static bool IsJumping(this PlayerState state) => state == PlayerState.Jumping; public static bool IsAttacking(this PlayerState state) => state == PlayerState.Attacking; public static bool HasIdle(this PlayerState state) => state == PlayerState.Idle; public static bool HasWalking(this PlayerState state) => state == PlayerState.Walking; // ... 其他 Has 方法 public static bool In(this PlayerState state, params PlayerState[] values) { return values.Contains(state); } public static string ToDisplayString(this PlayerState state) { return state switch { PlayerState.Idle => "Idle", PlayerState.Walking => "Walking", PlayerState.Running => "Running", PlayerState.Jumping => "Jumping", PlayerState.Attacking => "Attacking", _ => state.ToString() }; } } ``` ### 位标志枚举支持 ```csharp [GenerateEnumExtensions] [Flags] public enum PlayerAbilities { None = 0, Jump = 1 << 0, Run = 1 << 1, Attack = 1 << 2, Defend = 1 << 3, Magic = 1 << 4 } // 生成位标志扩展方法 public static class PlayerAbilitiesExtensions { public static bool HasJump(this PlayerAbilities abilities) => abilities.HasFlag(PlayerAbilities.Jump); public static bool HasRun(this PlayerAbilities abilities) => abilities.HasFlag(PlayerAbilities.Run); // ... 其他位标志方法 public static bool HasAny(this PlayerAbilities abilities, params PlayerAbilities[] flags) { return flags.Any(flag => abilities.HasFlag(flag)); } public static bool HasAll(this PlayerAbilities abilities, params PlayerAbilities[] flags) { return flags.All(flag => abilities.HasFlag(flag)); } } ``` ### 配置选项说明 | 参数 | 类型 | 默认值 | 说明 | |---------------------|--------|-------|------------------------| | `generateIsMethods` | bool | true | 是否生成 IsX() 方法 | | `generateHasMethod` | bool | true | 是否生成 HasX() 方法 | | `generateInMethod` | bool | true | 是否生成 In(params T[]) 方法 | | `customPrefix` | string | "Is" | 方法名前缀 | | `includeToString` | bool | false | 是否生成 ToString 扩展 | | `namespace` | string | null | 生成扩展类的命名空间 | ## 诊断信息 GFramework.SourceGenerators 提供详细的编译时诊断信息,帮助开发者快速定位和解决问题。 ### GF_Logging_001 - 日志字段名冲突 ```csharp [Log(fieldName = "Logger")] public partial class ClassWithLogger { private readonly ILogger Logger; // ❌ 冲突! } ``` **错误信息**: `GF_Logging_001: Logger field name 'Logger' conflicts with existing field` **解决方案**: 更改字段名或移除冲突字段 ```csharp [Log(fieldName = "CustomLogger")] public partial class ClassWithLogger { private readonly ILogger Logger; // ✅ 不冲突 private static readonly ILogger CustomLogger; // ✅ 生成器使用 CustomLogger } ``` ### GF_Rule_001 - ContextAware 接口冲突 ```csharp [ContextAware] public partial class AlreadyContextAware : IContextAware // ❌ 冲突! { // 已实现 IContextAware } ``` **错误信息**: `GF_Rule_001: Type already implements IContextAware interface` **解决方案**: 移除 [ContextAware] 属性或移除手动实现 ```csharp // 方案1:移除属性 public partial class AlreadyContextAware : IContextAware { // 手动实现 } // 方案2:移除手动实现,使用生成器 [ContextAware] public partial class AlreadyContextAware { // 生成器自动实现 } ``` ### GF_Enum_001 - 枚举成员命名冲突 ```csharp [GenerateEnumExtensions] public enum ConflictEnum { IsPlaying, // ❌ 冲突!会生成 IsIsPlaying() HasJump // ❌ 冲突!会生成 HasHasJump() } ``` **错误信息**: `GF_Enum_001: Enum member name conflicts with generated method` **解决方案**: 重命名枚举成员或自定义前缀 ```csharp [GenerateEnumExtensions(customPrefix = "IsState")] public enum ConflictEnum { Playing, // ✅ 生成 IsStatePlaying() Jump // ✅ 生成 IsStateJump() } ``` ## 性能优势 ### 编译时 vs 运行时对比 | 特性 | 手动实现 | 反射实现 | 源码生成器 | |-----------|------|------|-------| | **运行时性能** | 最优 | 最差 | 最优 | | **内存开销** | 最小 | 最大 | 最小 | | **类型安全** | 编译时 | 运行时 | 编译时 | | **开发效率** | 低 | 中 | 高 | | **调试友好** | 好 | 差 | 好 | ### 基准测试结果 ```csharp // 日志性能对比 (100,000 次调用) // 手动实现: 0.23ms // 反射实现: 45.67ms // 源码生成器: 0.24ms (几乎无差异) // 上下文访问性能对比 (1,000,000 次访问) // 手动实现: 0.12ms // 反射实现: 23.45ms // 源码生成器: 0.13ms (几乎无差异) ``` ### 内存分配分析 ```csharp // 使用 source generators 的内存分配 [Log] [ContextAware] public partial class EfficientController : IController { public void Process() { Logger.Info("Processing"); // 0 分配 var model = Context.GetModel(); // 0 分配 } } // 对比反射实现的内存分配 public class InefficientController : IController { public void Process() { var logger = GetLoggerViaReflection(); // 每次分配 var model = GetModelViaReflection(); // 每次分配 } } ``` ## 使用示例 ### 完整的游戏控制器示例 ```csharp using GFramework.SourceGenerators.Attributes; using GFramework.Core.Abstractions; [Log] [ContextAware] public partial class GameController : Node, IController { private PlayerModel _playerModel; private CombatSystem _combatSystem; public override void _Ready() { // 初始化模型和系统引用 _playerModel = Context.GetModel(); _combatSystem = Context.GetSystem(); // 监听事件 this.RegisterEvent(OnPlayerInput) .UnRegisterWhenNodeExitTree(this); Logger.Info("Game controller initialized"); } private void OnPlayerInput(PlayerInputEvent e) { Logger.Debug($"Processing player input: {e.Action}"); switch (e.Action) { case "attack": HandleAttack(); break; case "defend": HandleDefend(); break; } } private void HandleAttack() { if (_playerModel.CanAttack()) { Logger.Info("Player attacks"); _combatSystem.ProcessAttack(); Context.SendEvent(new AttackEvent()); } else { Logger.Warning("Player cannot attack - cooldown"); } } private void HandleDefend() { if (_playerModel.CanDefend()) { Logger.Info("Player defends"); _playerModel.IsDefending.Value = true; Context.SendEvent(new DefendEvent()); } else { Logger.Warning("Player cannot defend"); } } } ``` ### 枚举状态管理示例 ```csharp [GenerateEnumExtensions( generateIsMethods = true, generateHasMethod = true, generateInMethod = true, includeToString = true )] public enum CharacterState { Idle, Walking, Running, Jumping, Falling, Attacking, Hurt, Dead } [Log] [ContextAware] public partial class CharacterController : Node, IController { private CharacterModel _characterModel; public override void _Ready() { _characterModel = Context.GetModel(); // 监听状态变化 _characterModel.State.Register(OnStateChanged); } private void OnStateChanged(CharacterState newState) { Logger.Info($"Character state changed to: {newState.ToDisplayString()}"); // 使用生成的扩展方法 if (newState.IsDead()) { HandleDeath(); } else if (newState.IsHurt()) { HandleHurt(); } else if (newState.In(CharacterState.Walking, CharacterState.Running)) { StartMovementEffects(); } // 检查是否可以接受输入 if (newState.In(CharacterState.Idle, CharacterState.Walking, CharacterState.Running)) { EnableInput(); } else { DisableInput(); } } private bool CanAttack() { var state = _characterModel.State.Value; return state.In(CharacterState.Idle, CharacterState.Walking, CharacterState.Running); } private void HandleDeath() { Logger.Info("Character died"); DisableInput(); PlayDeathAnimation(); Context.SendEvent(new CharacterDeathEvent()); } } ``` ## 最佳实践 ### 🎯 属性使用策略 #### 1. 合理的属性组合 ```csharp // 好的做法:相关功能组合使用 [Log] [ContextAware] public partial class BusinessLogicComponent : IComponent { // 既有日志记录又有上下文访问 } // 避免:不必要的属性 [Log] // ❌ 静态工具类通常不需要日志 public static class MathHelper { public static int Add(int a, int b) => a + b; } ``` #### 2. 命名约定 ```csharp // 好的做法:一致的命名 [Log(fieldName = "Logger")] public partial class PlayerController { } [Log(fieldName = "Logger")] public partial class EnemyController { } // 避免:不一致的命名 [Log(fieldName = "Logger")] public partial class PlayerController { } [Log(fieldName = "CustomLogger")] public partial class EnemyController { } ``` ### 🏗️ 项目组织 #### 1. 分离生成器和业务逻辑 ```csharp // 好的做法:部分类分离 // PlayerController.Logic.cs - 业务逻辑 public partial class PlayerController : IController { public void Move(Vector2 direction) { if (CanMove()) { UpdatePosition(direction); Logger.Debug($"Player moved to {direction}"); } } } // PlayerController.Generated.cs - 生成代码所在 // 不需要手动维护,由生成器处理 ``` #### 2. 枚举设计 ```csharp // 好的做法:有意义的枚举设计 [GenerateEnumExtensions] public enum GameState { MainMenu, // 主菜单 Playing, // 游戏中 Paused, // 暂停 GameOver, // 游戏结束 Victory // 胜利 } // 避免:含义不明确的枚举值 [GenerateEnumExtensions] public enum State { State1, State2, State3 } ``` ### 🔧 性能优化 #### 1. 避免过度日志 ```csharp // 好的做法:合理的日志级别 [Log(minLevel = LogLevel.Information)] public partial class PerformanceCriticalComponent { public void Update() { // 只在必要时记录日志 if (_performanceCounter % 1000 == 0) { Logger.Debug($"Performance: {_performanceCounter} ticks"); } } } // 避免:过度日志记录 [Log(minLevel = LogLevel.Debug)] public partial class NoisyComponent { public void Update() { Logger.Debug($"Frame: {Engine.GetProcessFrames()}"); // 太频繁 } } ``` #### 2. 延迟上下文初始化 ```csharp // 好的做法:延迟初始化 [ContextAware(useLazy = true)] public partial class LazyContextComponent : IComponent { // 只有在第一次访问 Context 时才会初始化 public void Initialize() { // 如果这里不需要 Context,就不会初始化 SomeOtherInitialization(); } } ``` ### 🛡️ 错误处理 #### 1. 上下文验证 ```csharp [ContextAware(validateContext = true)] public partial class SafeContextComponent : IComponent { public void ProcessData() { if (Context.IsInvalid) { Logger.Error("Context is invalid, cannot process data"); return; } // 安全地使用 Context var model = Context.GetModel(); // ... } } ``` #### 2. 异常处理配合 ```csharp [Log] [ContextAware] public partial class RobustComponent : IComponent { public void RiskyOperation() { try { var model = Context.GetModel(); model.PerformRiskyOperation(); Logger.Info("Operation completed successfully"); } catch (Exception ex) { Logger.Error($"Operation failed: {ex.Message}"); Context.SendEvent(new OperationFailedEvent { Error = ex.Message }); } } } ``` ## 常见问题 ### Q: 为什么需要标记类为 `partial`? **A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。生成器会在编译时创建另一个部分类文件,包含生成的代码。 ```csharp [Log] public partial class MyClass { } // ✅ 需要 partial [Log] public class MyClass { } // ❌ 编译错误,无法添加生成代码 ``` ### Q: 生成的代码在哪里? **A**: 生成的代码在编译过程中创建,默认位置在 `obj/Debug/net6.0/generated/` 目录下。可以在项目文件中配置输出位置: ```xml Generated ``` ### Q: 如何调试生成器问题? **A**: 生成器提供了详细的诊断信息: 1. **查看错误列表**:编译错误会显示在 IDE 中 2. **查看生成文件**:检查生成的代码文件 3. **启用详细日志**:在项目文件中添加: ```xml true ``` ### Q: 可以自定义生成器吗? **A**: 当前版本的生成器支持有限的配置。如需完全自定义,可以创建自己的源代码生成器项目。 ### Q: 性能影响如何? **A**: 源代码生成器对运行时性能的影响几乎为零: - **编译时**:可能会增加编译时间(通常几秒) - **运行时**:与手写代码性能相同 - **内存使用**:与手写代码内存使用相同 ### Q: 与依赖注入框架兼容吗? **A**: 完全兼容。生成器创建的是标准代码,可以与任何依赖注入框架配合使用: ```csharp [Log] [ContextAware] public partial class ServiceComponent : IService { // 可以通过构造函数注入依赖 private readonly IDependency _dependency; public ServiceComponent(IDependency dependency) { _dependency = dependency; Logger.Info("Service initialized with dependency injection"); } } ``` --- ## 依赖关系 ```mermaid graph TD A[GFramework.SourceGenerators] --> B[GFramework.SourceGenerators.Abstractions] A --> C[GFramework.SourceGenerators.Common] A --> D[GFramework.Core.Abstractions] A --> E[Microsoft.CodeAnalysis.CSharp] A --> F[Microsoft.CodeAnalysis.Analyzers] ``` ## 版本兼容性 - **.NET**: 6.0+ - **Visual Studio**: 2022 17.0+ - **Rider**: 2022.3+ - **Roslyn**: 4.0+ ## 许可证 本项目基于 Apache 2.0 许可证 - 详情请参阅 [LICENSE](../LICENSE) 文件。 --- **版本**: 1.0.0 **更新日期**: 2026-01-12