docs(guide): 添加 ContextAware 生成器文档并更新相关链接

- 新增 ContextAware 生成器完整文档,介绍自动实现 IContextAware 接口的功能
- 更新索引页面中的相关链接,替换规则生成器为 ContextAware 生成器
- 修改基础使用示例中的命名空间引用和代码实现细节
- 补充测试场景配置和多架构场景的使用说明
- 添加最佳实践和诊断信息相关内容
This commit is contained in:
GeWuYou 2026-03-07 22:50:59 +08:00
parent 575dcdf27b
commit 6d398a515b
5 changed files with 444 additions and 202 deletions

View File

@ -0,0 +1,378 @@
# 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 void 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
// 推荐需要生命<E7949F><E591BD><EFBFBD>期管理时继承基类
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)
- [规则验证生成器](./rule-generator)

View File

@ -170,4 +170,4 @@ public enum Difficulty
- [Source Generators 概述](./index)
- [日志生成器](./logging-generator)
- [规则生成器](./rule-generator)
- [ContextAware 生成器](./context-aware-generator)

View File

@ -184,8 +184,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
@ -195,7 +195,7 @@ public partial class PlayerController : IController
// Context 属性自动生成,提供架构上下文访问
var playerModel = Context.GetModel<PlayerModel>();
var combatSystem = Context.GetSystem<CombatSystem>();
Context.SendEvent(new PlayerInitializedEvent());
}
}
@ -207,57 +207,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)
{
_context = context;
}
public IContextAware.Context GetContext()
get
{
if (_context == null)
{
_contextProvider ??= new global::GFramework.Core.architecture.GameContextProvider();
_context = _contextProvider.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();
}
}
```

View File

@ -157,4 +157,4 @@ public partial class MyClass
- [Source Generators 概述](./index)
- [枚举扩展生成器](./enum-generator)
- [规则生成器](./rule-generator)
- [ContextAware 生成器](./context-aware-generator)

View File

@ -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)