diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index c6802dc..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "enabledPlugins": { - "oh-my-claudecode@omc": true - } -} diff --git a/.gitignore b/.gitignore index b27afa0..702e68b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ GFramework.sln.DotSettings.user # ai opencode.json .claude/settings.local.json +.claude/settings.json .omc/ docs/.omc/ docs/.vitepress/cache/ \ No newline at end of file diff --git a/docs/zh-CN/source-generators/enum-generator.md b/docs/zh-CN/source-generators/enum-generator.md index e5aa9c7..2e3b455 100644 --- a/docs/zh-CN/source-generators/enum-generator.md +++ b/docs/zh-CN/source-generators/enum-generator.md @@ -1,172 +1,235 @@ # 枚举扩展生成器 -> GFramework.SourceGenerators 自动生成枚举扩展方法 +> 自动为枚举类型生成扩展方法 ## 概述 -枚举扩展生成器为枚举类型自动生成常用的扩展方法,如获取描述、转换为字符串、解析等。这大大简化了枚举的操作。 +枚举扩展生成器为标记了 `[GenerateEnumExtensions]` 属性的枚举自动生成两种扩展方法: -## 基本用法 +1. **IsX 方法**:为每个枚举值生成判断方法 +2. **IsIn 方法**:判断枚举值是否在指定集合中 -### 标记枚举 +## 基础使用 ```csharp -using GFramework.SourceGenerators.Attributes; +using GFramework.SourceGenerators.Abstractions.enums; -[EnumExtensions] +[GenerateEnumExtensions] +public enum GameState +{ + Normal, + Paused, + GameOver +} +``` + +## 生成的代码 + +编译器会自动生成如下扩展方法: + +```csharp +// +public static class GameStateExtensions +{ + // 为每个枚举值生成 IsX 方法 + public static bool IsNormal(this GameState value) + => value == GameState.Normal; + + public static bool IsPaused(this GameState value) + => value == GameState.Paused; + + public static bool IsGameOver(this GameState value) + => value == GameState.GameOver; + + // 生成 IsIn 方法 + public static bool IsIn(this GameState value, params GameState[] values) + { + if (values == null) return false; + foreach (var v in values) if (value == v) return true; + return false; + } +} +``` + +## 使用示例 + +```csharp +var state = GameState.Paused; + +// 使用 IsX 方法 +if (state.IsPaused()) +{ + Console.WriteLine("游戏已暂停"); +} + +// 使用 IsIn 方法 +if (state.IsIn(GameState.Paused, GameState.GameOver)) +{ + Console.WriteLine("游戏未在运行中"); +} +``` + +## 配置选项 + +可以通过属性参数控制生成行为: + +```csharp +[GenerateEnumExtensions( + GenerateIsMethods = true, // 是否生成 IsX 方法(默认 true) + GenerateIsInMethod = true // 是否生成 IsIn 方法(默认 true) +)] +public enum GameState +{ + Normal, + Paused, + GameOver +} +``` + +### 只生成 IsX 方法 + +```csharp +[GenerateEnumExtensions(GenerateIsInMethod = false)] +public enum GameState +{ + Normal, + Paused +} + +// 只生成 IsNormal() 和 IsPaused(),不生成 IsIn() +``` + +### 只生成 IsIn 方法 + +```csharp +[GenerateEnumExtensions(GenerateIsMethods = false)] +public enum GameState +{ + Normal, + Paused +} + +// 只生成 IsIn(),不生成 IsNormal() 和 IsPaused() +``` + +## 最佳实践 + +### 1. 命名约定 + +生成的方法名基于枚举值名称: + +- `Normal` → `IsNormal()` +- `GameOver` → `IsGameOver()` +- `HTTP_ERROR` → `IsHTTP_ERROR()` + +### 2. 性能考虑 + +生成的方法是内联的,性能与手写代码相同: +```csharp +// 生成的代码 +public static bool IsNormal(this GameState value) + => value == GameState.Normal; + +// 等价于手写 +if (state == GameState.Normal) { } +``` + +### 3. 可读性提升 + +```csharp +// 使用生成的方法(推荐) +if (state.IsPaused()) +{ + ResumeGame(); +} + +// 直接比较(不推荐) +if (state == GameState.Paused) +{ + ResumeGame(); +} +``` + +## 实际应用示例 + +### 游戏状态管理 + +```csharp +[GenerateEnumExtensions] +public enum GameState +{ + MainMenu, + Playing, + Paused, + GameOver, + Victory +} + +public class GameManager +{ + private GameState _currentState = GameState.MainMenu; + + public bool CanProcessInput() + { + // 使用 IsIn 方法简化多值判断 + return _currentState.IsIn(GameState.Playing, GameState.MainMenu); + } + + public void Update() + { + // 使用 IsX 方法提高可读性 + if (_currentState.IsPlaying()) + { + UpdateGameLogic(); + } + else if (_currentState.IsPaused()) + { + UpdatePauseMenu(); + } + } +} +``` + +### 角色状态控制 + +```csharp +[GenerateEnumExtensions] public enum PlayerState { Idle, + Walking, Running, Jumping, Attacking } -``` -### 生成的方法 - -上面的代码会被转换为: - -```csharp -public static class PlayerStateExtensions +public class PlayerController { - public static string GetDescription(this PlayerState value) + private PlayerState _state = PlayerState.Idle; + + public bool CanAttack() { - // 返回枚举的描述 + // 清晰表达可以攻击的状态 + return _state.IsIn(PlayerState.Idle, PlayerState.Walking, PlayerState.Running); } - public static bool HasFlag(this PlayerState value, PlayerState flag) + public void HandleInput(string action) { - // 检查是否包含标志 - } - - public static PlayerState FromString(string value) - { - // 从字符串解析枚举 + if (action == "jump" && !_state.IsJumping()) + { + _state = PlayerState.Jumping; + } } } ``` -## 常用方法 +## 限制 -### 获取描述 +1. **只支持枚举类型**:不能用于类或结构体 +2. **不支持 Flags 枚举的特殊处理**:对于 `[Flags]` 枚举,生成的方法仍然是简单的相等比较 +3. **不生成其他方法**:只生成 IsX 和 IsIn 方法 -```csharp -[EnumExtensions] -public enum ItemQuality -{ - [Description("普通")] - Common, - - [Description("稀有")] - Rare, - - [Description("史诗")] - Epic -} - -public void PrintQuality(ItemQuality quality) -{ - // 获取描述文本 - Console.WriteLine(quality.GetDescription()); - // 输出: "普通" / "稀有" / "史诗" -} -``` - -### 安全解析 - -```csharp -public void ParseState(string input) -{ - // 安全地解析字符串为枚举 - if (PlayerState.Running.TryParse(input, out var state)) - { - Console.WriteLine($"状态: {state}"); - } -} -``` - -### 获取所有值 - -```csharp -public void ListAllStates() -{ - // 获取所有枚举值 - foreach (var state in PlayerState.GetAllValues()) - { - Console.WriteLine(state); - } -} -``` - -## 标志枚举 - -对于使用 `[Flags]` 特性的枚举: - -```csharp -[EnumExtensions] -[Flags] -public enum PlayerPermissions -{ - None = 0, - Read = 1, - Write = 2, - Execute = 4, - All = Read | Write | Execute -} - -public void CheckPermissions() -{ - var permissions = PlayerPermissions.Read | PlayerPermissions.Write; - - // 检查是否包含特定权限 - if (permissions.HasFlag(PlayerPermissions.Write)) - { - Console.WriteLine("有写入权限"); - } - - // 获取所有设置的标志 - foreach (var flag in permissions.GetFlags()) - { - Console.WriteLine($"权限: {flag}"); - } -} -``` - -## 自定义行为 - -### 忽略某些值 - -```csharp -[EnumExtensions(IgnoreValues = new[] { ItemQuality.Undefined })] -public enum ItemQuality -{ - Undefined, - Common, - Rare, - Epic -} - -// GetAllValues() 不会返回 Undefined -``` - -### 自定义转换 - -```csharp -[EnumExtensions(CaseSensitive = false)] -public enum Difficulty -{ - Easy, - Medium, - Hard -} - -// FromString("EASY") 也能正确解析 -``` - ---- - -**相关文档**: +## 相关文档 - [Source Generators 概述](./index) - [日志生成器](./logging-generator) diff --git a/docs/zh-CN/source-generators/index.md b/docs/zh-CN/source-generators/index.md index 36df3c1..c404cdb 100644 --- a/docs/zh-CN/source-generators/index.md +++ b/docs/zh-CN/source-generators/index.md @@ -107,46 +107,41 @@ public partial class PlayerController ```csharp // -using Microsoft.Extensions.Logging; - -namespace YourNamespace +public partial class PlayerController { - public partial class PlayerController - { - private static readonly ILogger Logger = - LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(); - } + private static readonly ILogger Logger = + LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.PlayerController"); } ``` +**注意**:生成器只生成 ILogger 字段,不生成日志方法。日志方法(Info、Debug、Error 等)来自 ILogger 接口本身。 + ### 高级配置 ```csharp [Log( - fieldName = "CustomLogger", // 自定义字段名 - accessModifier = AccessModifier.Public, // 访问修饰符 - isStatic = false, // 是否为静态字段 - loggerName = "Custom.PlayerLogger", // 自定义日志器名称 - includeLoggerInterface = true // 是否包含 ILogger 接口 + Name = "Custom.PlayerLogger", // 自定义日志分类名称 + FieldName = "CustomLogger", // 自定义字段名 + IsStatic = false, // 是否为静态字段 + AccessModifier = "public" // 访问修饰符 )] public partial class CustomLoggerExample { public void LogSomething() { - CustomLogger.LogInformation("Custom logger message"); + CustomLogger.Info("Custom logger message"); } } ``` ### 配置选项说明 -| 参数 | 类型 | 默认值 | 说明 | -|--------------------------|----------------|----------|---------------------| -| `fieldName` | string | "Logger" | 生成的日志字段名称 | -| `accessModifier` | AccessModifier | Private | 字段访问修饰符 | -| `isStatic` | bool | true | 是否生成静态字段 | -| `loggerName` | string | null | 自定义日志器名称,null 时使用类名 | -| `includeLoggerInterface` | bool | false | 是否包含 ILogger 接口实现 | +| 参数 | 类型 | 默认值 | 说明 | +|----------------|---------|-----------|---------------------------------| +| Name | string? | null | 日志分类名称(默认使用类名) | +| FieldName | string | "Logger" | 生成的字段名称 | +| IsStatic | bool | true | 是否生成静态字段 | +| AccessModifier | string | "private" | 访问修饰符(private/protected/public) | ### 静态类支持 @@ -162,21 +157,6 @@ public static partial class MathHelper } ``` -### 日志级别控制 - -```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 接口,提供便捷的架构上下文访问能力。 @@ -316,7 +296,7 @@ public partial class AdvancedController : IController ### 基础使用 ```csharp -using GFramework.SourceGenerators.Attributes; +using GFramework.SourceGenerators.Abstractions.enums; [GenerateEnumExtensions] public enum GameState @@ -334,10 +314,12 @@ public static class GameStateExtensions 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); + if (values == null) return false; + foreach (var v in values) if (state == v) return true; + return false; } } @@ -345,17 +327,17 @@ public static class GameStateExtensions 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); @@ -363,15 +345,12 @@ public class GameManager } ``` -### 自定义扩展方法 +### 配置选项 ```csharp [GenerateEnumExtensions( - generateIsMethods = true, - generateHasMethod = true, - generateInMethod = true, - customPrefix = "Is", - includeToString = true + GenerateIsMethods = true, // 是否生成 IsX 方法(默认 true) + GenerateIsInMethod = true // 是否生成 IsIn 方法(默认 true) )] public enum PlayerState { @@ -381,84 +360,14 @@ public enum PlayerState 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 | 生成扩展类的命名空间 | +| 参数 | 类型 | 默认值 | 说明 | +|--------------------|------|------|-------------------| +| GenerateIsMethods | bool | true | 是否为每个枚举值生成 IsX 方法 | +| GenerateIsInMethod | bool | true | 是否生成 IsIn 方法 | ## 诊断信息 @@ -839,7 +748,7 @@ public enum State ```csharp // 好的做法:合理的日志级别 -[Log(minLevel = LogLevel.Information)] +[Log] public partial class PerformanceCriticalComponent { public void Update() @@ -853,7 +762,7 @@ public partial class PerformanceCriticalComponent } // 避免:过度日志记录 -[Log(minLevel = LogLevel.Debug)] +[Log] public partial class NoisyComponent { public void Update() @@ -863,47 +772,8 @@ public partial class NoisyComponent } ``` -#### 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] diff --git a/docs/zh-CN/source-generators/logging-generator.md b/docs/zh-CN/source-generators/logging-generator.md index c72c9cf..88bbc44 100644 --- a/docs/zh-CN/source-generators/logging-generator.md +++ b/docs/zh-CN/source-generators/logging-generator.md @@ -4,22 +4,21 @@ ## 概述 -日志生成器是一个 Source Generator,它会自动为标记了 `[Log]` 特性的类生成 Logger 字段和日志方法调用。这消除了手动编写日志代码的需要,让开发者专注于业务逻辑。 +日志生成器是一个 Source Generator,它会自动为标记了 `[Log]` 特性的类生成 ILogger 字段。这消除了手动编写日志字段的需要,让开发者专注于业务逻辑。 ## 基本用法 ### 标记类 ```csharp -using GFramework.SourceGenerators.Attributes; +using GFramework.SourceGenerators.Abstractions.logging; [Log] public partial class MyService { public void DoSomething() { - // 自动生成 Logger 字段 - // 自动生成日志调用 + // 自动生成的 Logger 字段可直接使用 Logger.Info("执行操作"); } } @@ -30,31 +29,19 @@ public partial class MyService 上面的代码会被编译时转换为: ```csharp +// public partial class MyService { - // 自动生成的字段 - [CompilerGenerated] - private ILogger _logger; - - // 自动生成的属性 - [CompilerGenerated] - public ILogger Logger - { - get - { - if (_logger == null) - { - _logger = LoggerFactory.CreateLogger(); - } - return _logger; - } - } + private static readonly ILogger Logger = + LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.MyService"); } ``` +**注意**:生成器只生成 ILogger 字段,不生成日志方法。日志方法(Info、Debug、Error 等)来自 ILogger 接口本身。 + ## 日志级别 -生成的日志方法支持多种级别: +生成的 Logger 字段支持 ILogger 接口的所有方法: ```csharp [Log] @@ -83,7 +70,7 @@ public partial class MyClass ## 自定义日志类别 ```csharp -[Log(LogCategory.Gameplay)] +[Log("Gameplay")] public partial class GameplaySystem { // 日志会标记为 Gameplay 类别 @@ -94,6 +81,55 @@ public partial class GameplaySystem } ``` +## 配置选项 + +### 自定义字段名称 + +```csharp +[Log(FieldName = "_customLogger")] +public partial class MyClass +{ + public void DoSomething() + { + // 使用自定义字段名 + _customLogger.Info("使用自定义日志器"); + } +} +``` + +### 非静态字段 + +```csharp +[Log(IsStatic = false)] +public partial class InstanceLogger +{ + // 生成实例字段而非静态字段 + public void LogMessage() + { + Logger.Info("实例日志"); + } +} +``` + +### 访问修饰符 + +```csharp +[Log(AccessModifier = "protected")] +public partial class ProtectedLogger +{ + // 生成 protected 字段 +} +``` + +### 配置选项说明 + +| 参数 | 类型 | 默认值 | 说明 | +|----------------|---------|-----------|---------------------------------| +| Name | string? | null | 日志分类名称(默认使用类名) | +| FieldName | string | "Logger" | 生成的字段名称 | +| IsStatic | bool | true | 是否生成静态字段 | +| AccessModifier | string | "private" | 访问修饰符(private/protected/public) | + ## 与其他模块集成 ### 与 Godot 集成 @@ -123,34 +159,175 @@ public partial class MySystem : AbstractSystem } ``` -## 配置选项 +## 实际应用示例 -### 禁用自动生成 +### 游戏控制器 ```csharp -// 禁用自动日志调用生成 -[Log(AutoLog = false)] -public partial class MyClass +[Log] +[ContextAware] +public partial class PlayerController : IController { - // 仍会生成 Logger 字段,但不会自动生成日志调用 - public void DoSomething() + public void HandleInput(string action) { - // 需要手动调用 Logger - Logger.Info("手动日志"); + Logger.Debug($"处理输入: {action}"); + + switch (action) + { + case "jump": + Logger.Info("玩家跳跃"); + Jump(); + break; + case "attack": + Logger.Info("玩家攻击"); + Attack(); + break; + default: + Logger.Warning($"未知操作: {action}"); + break; + } + } + + private void Jump() + { + try + { + // 跳跃逻辑 + Logger.Debug("跳跃执行成功"); + } + catch (Exception ex) + { + Logger.Error($"跳跃失败: {ex.Message}"); + } } } ``` -### 自定义字段名称 +### 数据处理服务 ```csharp -[Log(FieldName = "_customLogger")] -public partial class MyClass +[Log("DataService")] +public partial class DataProcessor { - // Logger 字段名称为 _customLogger + public void ProcessData(string data) + { + Logger.Info($"开始处理数据,长度: {data.Length}"); + + if (string.IsNullOrEmpty(data)) + { + Logger.Warning("数据为空,跳过处理"); + return; + } + + try + { + // 处理逻辑 + Logger.Debug("数据处理中..."); + // ... + Logger.Info("数据处理完成"); + } + catch (Exception ex) + { + Logger.Error($"数据处理失败: {ex.Message}"); + throw; + } + } } ``` +## 最佳实践 + +### 1. 合理使用日志级别 + +```csharp +[Log] +public partial class BestPracticeExample +{ + public void ProcessRequest() + { + // Debug: 详细的调试信息 + Logger.Debug("开始处理请求"); + + // Info: 重要的业务流程信息 + Logger.Info("请求处理成功"); + + // Warning: 可恢复的异常情况 + Logger.Warning("缓存未命中,使用默认值"); + + // Error: 错误但不影响系统运行 + Logger.Error("处理失败,将重试"); + + // Critical: 严重错误,可能导致系统崩溃 + Logger.Critical("数据库连接失败"); + } +} +``` + +### 2. 避免过度日志 + +```csharp +[Log] +public partial class PerformanceExample +{ + private int _frameCount = 0; + + public void Update() + { + // 好的做法:定期记录 + if (_frameCount % 1000 == 0) + { + Logger.Debug($"已运行 {_frameCount} 帧"); + } + _frameCount++; + + // 避免:每帧都记录 + // Logger.Debug($"帧 {_frameCount}"); // ❌ 太频繁 + } +} +``` + +### 3. 结构化日志信息 + +```csharp +[Log] +public partial class StructuredLogging +{ + public void ProcessUser(int userId, string action) + { + // 好的做法:包含上下文信息 + Logger.Info($"用户操作 [UserId={userId}, Action={action}]"); + + // 避免:信息不完整 + // Logger.Info("用户操作"); // ❌ 缺少上下文 + } +} +``` + +## 常见问题 + +### Q: 为什么需要 partial 关键字? + +**A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。 + +### Q: 可以在静态类中使用吗? + +**A**: 可以,生成器会自动生成静态字段: + +```csharp +[Log] +public static partial class StaticHelper +{ + public static void DoSomething() + { + Logger.Info("静态方法日志"); + } +} +``` + +### Q: 如何自定义日志工厂? + +**A**: 通过配置 `LoggerFactoryResolver.Provider` 来自定义日志工厂实现。 + --- **相关文档**: