mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
Merge pull request #86 from GeWuYou/docs/technical-documentation-update
docs(guide): 添加 ContextAware 生成器文档并更新相关链接
This commit is contained in:
commit
8a070af51b
@ -1,5 +0,0 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"oh-my-claudecode@omc": true
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@ GFramework.sln.DotSettings.user
|
||||
# ai
|
||||
opencode.json
|
||||
.claude/settings.local.json
|
||||
.claude/settings.json
|
||||
.omc/
|
||||
docs/.omc/
|
||||
docs/.vitepress/cache/
|
||||
377
docs/zh-CN/source-generators/context-aware-generator.md
Normal file
377
docs/zh-CN/source-generators/context-aware-generator.md
Normal file
@ -0,0 +1,377 @@
|
||||
# ContextAware 生成器
|
||||
|
||||
> 自动实现 IContextAware 接口,提供架构上下文访问能力
|
||||
|
||||
## 概述
|
||||
|
||||
ContextAware 生成器为标记了 `[ContextAware]` 属性的类自动生成 `IContextAware` 接口实现,使类能够便捷地访问架构上下文(
|
||||
`IArchitectureContext`)。这是 GFramework 中最常用的源码生成器之一,几乎所有需要与架构交互的组件都会使用它。
|
||||
|
||||
### 核心功能
|
||||
|
||||
- **自动接口实现**:无需手动实现 `IContextAware` 接口的 `SetContext()` 和 `GetContext()` 方法
|
||||
- **懒加载上下文**:`Context` 属性在首次访问时自动初始化
|
||||
- **默认提供者**:使用 `GameContextProvider` 作为默认上下文提供者
|
||||
- **测试友好**:支持通过 `SetContextProvider()` 配置自定义上下文提供者
|
||||
|
||||
## 基础使用
|
||||
|
||||
### 标记类
|
||||
|
||||
使用 `[ContextAware]` 属性标记需要访问架构上下文的类:
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Abstractions.rule;
|
||||
using GFramework.Core.Abstractions.controller;
|
||||
|
||||
[ContextAware]
|
||||
public partial class PlayerController : IController
|
||||
{
|
||||
public void Initialize()
|
||||
{
|
||||
// Context 属性自动生成,提供架构上下文访问
|
||||
var playerModel = Context.GetModel<PlayerModel>();
|
||||
var combatSystem = Context.GetSystem<CombatSystem>();
|
||||
|
||||
Context.SendEvent(new PlayerInitializedEvent());
|
||||
}
|
||||
|
||||
public void Attack(Enemy target)
|
||||
{
|
||||
var damage = Context.GetUtility<DamageCalculator>().Calculate(this, target);
|
||||
Context.SendCommand(new DealDamageCommand(target, damage));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 必要条件
|
||||
|
||||
标记的类必须满足以下条件:
|
||||
|
||||
1. **必须是 `partial` 类**:生成器需要生成部分类代码
|
||||
2. **必须是 `class` 类型**:不能是 `struct` 或 `interface`
|
||||
|
||||
```csharp
|
||||
// ✅ 正确
|
||||
[ContextAware]
|
||||
public partial class MyController { }
|
||||
|
||||
// ❌ 错误:缺少 partial 关键字
|
||||
[ContextAware]
|
||||
public class MyController { }
|
||||
|
||||
// ❌ 错误:不能用于 struct
|
||||
[ContextAware]
|
||||
public partial struct MyStruct { }
|
||||
```
|
||||
|
||||
## 生成的代码
|
||||
|
||||
编译器会为标记的类自动生成以下代码:
|
||||
|
||||
```csharp
|
||||
// <auto-generated/>
|
||||
#nullable enable
|
||||
|
||||
namespace YourNamespace;
|
||||
|
||||
partial class PlayerController : global::GFramework.Core.Abstractions.rule.IContextAware
|
||||
{
|
||||
private global::GFramework.Core.Abstractions.architecture.IArchitectureContext? _context;
|
||||
private static global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider? _contextProvider;
|
||||
|
||||
/// <summary>
|
||||
/// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider)
|
||||
/// </summary>
|
||||
protected global::GFramework.Core.Abstractions.architecture.IArchitectureContext Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
_contextProvider ??= new global::GFramework.Core.architecture.GameContextProvider();
|
||||
_context = _contextProvider.GetContext();
|
||||
}
|
||||
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置上下文提供者(用于测试或多架构场景)
|
||||
/// </summary>
|
||||
/// <param name="provider">上下文提供者实例</param>
|
||||
public static void SetContextProvider(global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider provider)
|
||||
{
|
||||
_contextProvider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置上下文提供者为默认值(用于测试清理)
|
||||
/// </summary>
|
||||
public static void ResetContextProvider()
|
||||
{
|
||||
_contextProvider = null;
|
||||
}
|
||||
|
||||
void global::GFramework.Core.Abstractions.rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.architecture.IArchitectureContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
global::GFramework.Core.Abstractions.architecture.IArchitectureContext global::GFramework.Core.Abstractions.rule.IContextAware.GetContext()
|
||||
{
|
||||
return Context;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 代码解析
|
||||
|
||||
生成的代码包含以下关键部分:
|
||||
|
||||
1. **私有字段**:
|
||||
- `_context`:缓存的上下文实例
|
||||
- `_contextProvider`:静态上下文提供者(所有实例共享)
|
||||
|
||||
2. **Context 属性**:
|
||||
- `protected` 访问级别,子类可访问
|
||||
- 懒加载:首次访问时自动初始化
|
||||
- 使用 `GameContextProvider` 作为默认提供者
|
||||
|
||||
3. **配置方法**:
|
||||
- `SetContextProvider()`:设置自定义上下文提供者
|
||||
- `ResetContextProvider()`:重置为默认提供者
|
||||
|
||||
4. **显式接口实现**:
|
||||
- `IContextAware.SetContext()`:允许外部设置上下文
|
||||
- `IContextAware.GetContext()`:返回当前上下文
|
||||
|
||||
## 配置上下文提供者
|
||||
|
||||
### 测试场景
|
||||
|
||||
在单元测试中,通常需要使用自定义的上下文提供者:
|
||||
|
||||
```csharp
|
||||
[Test]
|
||||
public async Task TestPlayerController()
|
||||
{
|
||||
// 创建测试架构
|
||||
var testArchitecture = new TestArchitecture();
|
||||
await testArchitecture.InitAsync();
|
||||
|
||||
// 配置自定义上下文提供者
|
||||
PlayerController.SetContextProvider(new TestContextProvider(testArchitecture));
|
||||
|
||||
try
|
||||
{
|
||||
// 测试代码
|
||||
var controller = new PlayerController();
|
||||
controller.Initialize();
|
||||
|
||||
// 验证...
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 清理:重置上下文提供者
|
||||
PlayerController.ResetContextProvider();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 多架构场景
|
||||
|
||||
在某些高级场景中,可能需要同时运行多个架构实例:
|
||||
|
||||
```csharp
|
||||
public class MultiArchitectureManager
|
||||
{
|
||||
private readonly Dictionary<string, IArchitecture> _architectures = new();
|
||||
|
||||
public void SwitchToArchitecture(string name)
|
||||
{
|
||||
var architecture = _architectures[name];
|
||||
var provider = new ScopedContextProvider(architecture);
|
||||
|
||||
// 为所有使用 [ContextAware] 的类切换上下文
|
||||
PlayerController.SetContextProvider(provider);
|
||||
EnemyController.SetContextProvider(provider);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 何时使用 [ContextAware]
|
||||
|
||||
推荐在以下场景使用 `[ContextAware]` 属性:
|
||||
|
||||
1. **Controller 层**:需要协调多个 Model/System 的控制器
|
||||
2. **Command/Query 实现**:需要访问架构服务的命令或查询
|
||||
3. **自定义组件**:不继承框架基类但需要上下文访问的组件
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial class GameFlowController : IController
|
||||
{
|
||||
public async Task StartGame()
|
||||
{
|
||||
var saveSystem = Context.GetSystem<SaveSystem>();
|
||||
var uiSystem = Context.GetSystem<UISystem>();
|
||||
|
||||
await saveSystem.LoadAsync();
|
||||
await uiSystem.ShowMainMenuAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 何时继承 ContextAwareBase
|
||||
|
||||
如果类需要更多框架功能(如生命周期管理),应继承 `ContextAwareBase`:
|
||||
|
||||
```csharp
|
||||
// 推荐:需要生命周期管理时继承基类
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
// AbstractModel 已经继承了 ContextAwareBase
|
||||
protected override void OnInit()
|
||||
{
|
||||
var config = Context.GetUtility<ConfigLoader>().Load<PlayerConfig>();
|
||||
}
|
||||
}
|
||||
|
||||
// 推荐:简单组件使用属性
|
||||
[ContextAware]
|
||||
public partial class SimpleHelper
|
||||
{
|
||||
public void DoSomething()
|
||||
{
|
||||
Context.SendEvent(new SomethingHappenedEvent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 与 IContextAware 接口的关系
|
||||
|
||||
生成的代码实现了 `IContextAware` 接口:
|
||||
|
||||
```csharp
|
||||
namespace GFramework.Core.Abstractions.rule;
|
||||
|
||||
public interface IContextAware
|
||||
{
|
||||
void SetContext(IArchitectureContext context);
|
||||
IArchitectureContext GetContext();
|
||||
}
|
||||
```
|
||||
|
||||
这意味着标记了 `[ContextAware]` 的类可以:
|
||||
|
||||
1. **被架构自动注入上下文**:实现 `IContextAware` 的类在注册到架构时会自动调用 `SetContext()`
|
||||
2. **参与依赖注入**:可以作为 `IContextAware` 类型注入到其他组件
|
||||
3. **支持上下文传递**:可以通过 `GetContext()` 将上下文传递给其他组件
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 始终使用 partial 关键字
|
||||
|
||||
```csharp
|
||||
// ✅ 正确
|
||||
[ContextAware]
|
||||
public partial class MyController { }
|
||||
|
||||
// ❌ 错误:编译器会报错
|
||||
[ContextAware]
|
||||
public class MyController { }
|
||||
```
|
||||
|
||||
### 2. 在测试中清理上下文提供者
|
||||
|
||||
```csharp
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// 避免测试之间的状态污染
|
||||
PlayerController.ResetContextProvider();
|
||||
EnemyController.ResetContextProvider();
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 避免在构造函数中访问 Context
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial class MyController
|
||||
{
|
||||
// ❌ 错误:构造函数执行时上下文可能未初始化
|
||||
public MyController()
|
||||
{
|
||||
var model = Context.GetModel<SomeModel>(); // 可能为 null
|
||||
}
|
||||
|
||||
// ✅ 正确:在初始化方法中访问
|
||||
public void Initialize()
|
||||
{
|
||||
var model = Context.GetModel<SomeModel>(); // 安全
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 优先使用 Context 属性而非接口方法
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial class MyController
|
||||
{
|
||||
public void DoSomething()
|
||||
{
|
||||
// ✅ 推荐:使用生成的 Context 属性
|
||||
var model = Context.GetModel<SomeModel>();
|
||||
|
||||
// ❌ 不推荐:显式调用接口方法
|
||||
var context = ((IContextAware)this).GetContext();
|
||||
var model2 = context.GetModel<SomeModel>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 诊断信息
|
||||
|
||||
生成器会在以下情况报告编译错误:
|
||||
|
||||
### GFSG001: 类必须是 partial
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public class MyController { } // 错误:缺少 partial 关键字
|
||||
```
|
||||
|
||||
**解决方案**:添加 `partial` 关键字
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial class MyController { } // ✅ 正确
|
||||
```
|
||||
|
||||
### GFSG002: ContextAware 只能用于类
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial struct MyStruct { } // 错误:不能用于 struct
|
||||
```
|
||||
|
||||
**解决方案**:将 `struct` 改为 `class`
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial class MyClass { } // ✅ 正确
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Source Generators 概述](./index)
|
||||
- [架构上下文](../core/context)
|
||||
- [IContextAware 接口](../core/rule)
|
||||
- [日志生成器](./logging-generator)
|
||||
@ -1,173 +1,236 @@
|
||||
# 枚举扩展生成器
|
||||
|
||||
> 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
|
||||
// <auto-generated/>
|
||||
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)
|
||||
- [规则生成器](./rule-generator)
|
||||
- [ContextAware 生成器](./context-aware-generator)
|
||||
|
||||
@ -107,46 +107,41 @@ public partial class PlayerController
|
||||
|
||||
```csharp
|
||||
// <auto-generated/>
|
||||
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<PlayerController>();
|
||||
}
|
||||
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 接口,提供便捷的架构上下文访问能力。
|
||||
@ -184,8 +164,8 @@ public partial class WarningOnlyLogger
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
using GFramework.Core.Abstractions;
|
||||
using GFramework.SourceGenerators.Abstractions.rule;
|
||||
using GFramework.Core.Abstractions.controller;
|
||||
|
||||
[ContextAware]
|
||||
public partial class PlayerController : IController
|
||||
@ -207,57 +187,84 @@ public partial class PlayerController : IController
|
||||
|
||||
```csharp
|
||||
// <auto-generated/>
|
||||
using GFramework.Core.Abstractions;
|
||||
#nullable enable
|
||||
|
||||
namespace YourNamespace
|
||||
namespace YourNamespace;
|
||||
|
||||
partial class PlayerController : global::GFramework.Core.Abstractions.rule.IContextAware
|
||||
{
|
||||
public partial class PlayerController : IContextAware
|
||||
private global::GFramework.Core.Abstractions.architecture.IArchitectureContext? _context;
|
||||
private static global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider? _contextProvider;
|
||||
|
||||
/// <summary>
|
||||
/// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider)
|
||||
/// </summary>
|
||||
protected global::GFramework.Core.Abstractions.architecture.IArchitectureContext Context
|
||||
{
|
||||
private IContextAware.Context _context;
|
||||
|
||||
public IContextAware.Context Context => _context ??= new LazyContext(this);
|
||||
|
||||
public void SetContext(IContextAware.Context context)
|
||||
get
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
if (_context == null)
|
||||
{
|
||||
_contextProvider ??= new global::GFramework.Core.architecture.GameContextProvider();
|
||||
_context = _contextProvider.GetContext();
|
||||
}
|
||||
|
||||
public IContextAware.Context GetContext()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 延迟初始化
|
||||
|
||||
```csharp
|
||||
[ContextAware(useLazy = true)]
|
||||
public partial class LazyContextExample
|
||||
{
|
||||
public void AccessContext()
|
||||
/// <summary>
|
||||
/// 配置上下文提供者(用于测试或多架构场景)
|
||||
/// </summary>
|
||||
public static void SetContextProvider(global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider provider)
|
||||
{
|
||||
// Context 会延迟初始化,直到第一次访问
|
||||
var model = Context.GetModel<SomeModel>();
|
||||
_contextProvider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置上下文提供者为默认值(用于测试清理)
|
||||
/// </summary>
|
||||
public static void ResetContextProvider()
|
||||
{
|
||||
_contextProvider = null;
|
||||
}
|
||||
|
||||
void global::GFramework.Core.Abstractions.rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.architecture.IArchitectureContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
global::GFramework.Core.Abstractions.architecture.IArchitectureContext global::GFramework.Core.Abstractions.rule.IContextAware.GetContext()
|
||||
{
|
||||
return Context;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 上下文验证
|
||||
### 测试场景配置
|
||||
|
||||
在单元测试中,可以配置自定义的上下文提供者:
|
||||
|
||||
```csharp
|
||||
[ContextAware(validateContext = true)]
|
||||
public partial class ValidatedContextExample
|
||||
[Test]
|
||||
public async Task TestPlayerController()
|
||||
{
|
||||
public void AccessContext()
|
||||
var testArchitecture = new TestArchitecture();
|
||||
await testArchitecture.InitAsync();
|
||||
|
||||
// 配置测试上下文提供者
|
||||
PlayerController.SetContextProvider(new TestContextProvider(testArchitecture));
|
||||
|
||||
try
|
||||
{
|
||||
// 每次访问都会验证上下文的有效性
|
||||
var model = Context.GetModel<SomeModel>();
|
||||
if (Context.IsInvalid)
|
||||
{
|
||||
throw new InvalidOperationException("Context is invalid");
|
||||
}
|
||||
var controller = new PlayerController();
|
||||
controller.Initialize();
|
||||
// 测试逻辑...
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 清理:重置上下文提供者
|
||||
PlayerController.ResetContextProvider();
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -289,7 +296,7 @@ public partial class AdvancedController : IController
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
using GFramework.SourceGenerators.Abstractions.enums;
|
||||
|
||||
[GenerateEnumExtensions]
|
||||
public enum GameState
|
||||
@ -310,7 +317,9 @@ public static class GameStateExtensions
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,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
|
||||
{
|
||||
@ -354,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 方法 |
|
||||
|
||||
## 诊断信息
|
||||
|
||||
@ -812,7 +748,7 @@ public enum State
|
||||
|
||||
```csharp
|
||||
// 好的做法:合理的日志级别
|
||||
[Log(minLevel = LogLevel.Information)]
|
||||
[Log]
|
||||
public partial class PerformanceCriticalComponent
|
||||
{
|
||||
public void Update()
|
||||
@ -826,7 +762,7 @@ public partial class PerformanceCriticalComponent
|
||||
}
|
||||
|
||||
// 避免:过度日志记录
|
||||
[Log(minLevel = LogLevel.Debug)]
|
||||
[Log]
|
||||
public partial class NoisyComponent
|
||||
{
|
||||
public void Update()
|
||||
@ -836,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<DataModel>();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 异常处理配合
|
||||
|
||||
```csharp
|
||||
[Log]
|
||||
[ContextAware]
|
||||
|
||||
@ -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
|
||||
// <auto-generated/>
|
||||
public partial class MyService
|
||||
{
|
||||
// 自动生成的字段
|
||||
[CompilerGenerated]
|
||||
private ILogger _logger;
|
||||
|
||||
// 自动生成的属性
|
||||
[CompilerGenerated]
|
||||
public ILogger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_logger == null)
|
||||
{
|
||||
_logger = LoggerFactory.CreateLogger<MyService>();
|
||||
}
|
||||
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,38 +159,179 @@ 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` 来自定义日志工厂实现。
|
||||
|
||||
---
|
||||
|
||||
**相关文档**:
|
||||
|
||||
- [Source Generators 概述](./index)
|
||||
- [枚举扩展生成器](./enum-generator)
|
||||
- [规则生成器](./rule-generator)
|
||||
- [ContextAware 生成器](./context-aware-generator)
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
# 规则生成器
|
||||
|
||||
> GFramework.SourceGenerators 自动生成规则验证代码
|
||||
|
||||
## 概述
|
||||
|
||||
规则生成器为实现了规则接口的类型自动生成验证方法。这使得规则定义更加简洁,并确保规则的一致性。
|
||||
|
||||
## 基本用法
|
||||
|
||||
### 定义规则
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
|
||||
[RuleFor(typeof(Player))]
|
||||
public class PlayerRule : IRule<Player>
|
||||
{
|
||||
public RuleResult Validate(Player player)
|
||||
{
|
||||
if (player.Health <= 0)
|
||||
{
|
||||
return RuleResult.Invalid("玩家生命值不能为零或负数");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(player.Name))
|
||||
{
|
||||
return RuleResult.Invalid("玩家名称不能为空");
|
||||
}
|
||||
|
||||
return RuleResult.Valid();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用生成的验证器
|
||||
|
||||
```csharp
|
||||
public class PlayerValidator
|
||||
{
|
||||
// 自动生成 Validate 方法
|
||||
public void ValidatePlayer(Player player)
|
||||
{
|
||||
var result = PlayerRuleValidator.Validate(player);
|
||||
|
||||
if (!result.IsValid)
|
||||
{
|
||||
Console.WriteLine($"验证失败: {result.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 组合规则
|
||||
|
||||
### 多规则组合
|
||||
|
||||
```csharp
|
||||
[RuleFor(typeof(Player), RuleCombinationType.And)]
|
||||
public class PlayerHealthRule : IRule<Player>
|
||||
{
|
||||
public RuleResult Validate(Player player)
|
||||
{
|
||||
if (player.Health <= 0)
|
||||
return RuleResult.Invalid("生命值必须大于0");
|
||||
return RuleResult.Valid();
|
||||
}
|
||||
}
|
||||
|
||||
[RuleFor(typeof(Player), RuleCombinationType.And)]
|
||||
public class PlayerNameRule : IRule<Player>
|
||||
{
|
||||
public RuleResult Validate(Player player)
|
||||
{
|
||||
if (string.IsNullOrEmpty(player.Name))
|
||||
return RuleResult.Invalid("名称不能为空");
|
||||
return RuleResult.Valid();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 验证所有规则
|
||||
|
||||
```csharp
|
||||
public void ValidateAll(Player player)
|
||||
{
|
||||
var results = PlayerRuleValidator.ValidateAll(player);
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (!result.IsValid)
|
||||
{
|
||||
Console.WriteLine(result.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 异步规则
|
||||
|
||||
```csharp
|
||||
[RuleFor(typeof(Player), IsAsync = true)]
|
||||
public class AsyncPlayerRule : IAsyncRule<Player>
|
||||
{
|
||||
public async Task<RuleResult> ValidateAsync(Player player)
|
||||
{
|
||||
// 异步验证,如检查服务器
|
||||
var isBanned = await CheckBanStatus(player.Id);
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
return RuleResult.Invalid("玩家已被封禁");
|
||||
}
|
||||
|
||||
return RuleResult.Valid();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 单一职责
|
||||
|
||||
```csharp
|
||||
// 推荐:每个规则只验证一个方面
|
||||
[RuleFor(typeof(Player))]
|
||||
public class PlayerHealthRule : IRule<Player> { }
|
||||
|
||||
[RuleFor(typeof(Player))]
|
||||
public class PlayerNameRule : IRule<Player> { }
|
||||
|
||||
// 避免:在一个规则中验证多个方面
|
||||
[RuleFor(typeof(Player))]
|
||||
public class PlayerMegaRule : IRule<Player>
|
||||
{
|
||||
public RuleResult Validate(Player player)
|
||||
{
|
||||
// 验证健康、名称、等级...
|
||||
// 不要这样设计
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 清晰的错误信息
|
||||
|
||||
```csharp
|
||||
public RuleResult Validate(Player player)
|
||||
{
|
||||
// 推荐:具体的错误信息
|
||||
return RuleResult.Invalid($"玩家 {player.Name} 的生命值 {player.Health} 不能小于 1");
|
||||
|
||||
// 避免:模糊的错误信息
|
||||
return RuleResult.Invalid("无效的玩家");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**相关文档**:
|
||||
|
||||
- [Source Generators 概述](./index)
|
||||
- [日志生成器](./logging-generator)
|
||||
- [枚举扩展生成器](./enum-generator)
|
||||
Loading…
x
Reference in New Issue
Block a user