GFramework/docs/zh-CN/source-generators/context-aware-generator.md
GeWuYou fb14d7122c docs(style): 更新文档中的命名空间导入格式
- 将所有小写的命名空间导入更正为首字母大写格式
- 统一 GFramework 框架的命名空间引用规范
- 修复 core、ecs、godot 等模块的命名空间导入错误
- 标准化文档示例代码中的 using 语句格式
- 确保所有文档中的命名空间引用保持一致性
- 更新 global using 语句以匹配正确的命名空间格式
2026-03-10 07:18:49 +08:00

404 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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()
{
// 使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口)
var playerModel = this.GetModel<PlayerModel>();
var combatSystem = this.GetSystem<CombatSystem>();
this.SendEvent(new PlayerInitializedEvent());
}
public void Attack(Enemy target)
{
var damage = this.GetUtility<DamageCalculator>().Calculate(this, target);
this.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 = this.GetSystem<SaveSystem>();
var uiSystem = this.GetSystem<UISystem>();
await saveSystem.LoadAsync();
await uiSystem.ShowMainMenuAsync();
}
}
```
### 与 IController 配合使用
在 Godot 项目中,控制器通常同时实现 `IController` 和使用 `[ContextAware]`
```csharp
using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware]
public partial class PlayerController : Node, IController
{
public override void _Ready()
{
// 使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口)
var playerModel = this.GetModel<PlayerModel>();
var combatSystem = this.GetSystem<CombatSystem>();
}
}
```
**说明**
- `IController` 是标记接口,标识这是一个控制器
- `[ContextAware]` 提供架构访问能力
- 两者配合使用是推荐的模式
### 何时继承 ContextAwareBase
如果类需要更多框架功能(如生命周期管理),应继承 `ContextAwareBase`
```csharp
// 推荐:需要生命周期管理时继承基类
public class PlayerModel : AbstractModel
{
// AbstractModel 已经继承了 ContextAwareBase
protected override void OnInit()
{
var config = this.GetUtility<ConfigLoader>().Load<PlayerConfig>();
}
}
// 推荐:简单组件使用属性
[ContextAware]
public partial class SimpleHelper
{
public void DoSomething()
{
this.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 = this.GetModel<SomeModel>(); // 可能为 null
}
// ✅ 正确:在初始化方法中访问
public void Initialize()
{
var model = this.GetModel<SomeModel>(); // 安全
}
}
```
### 4. 优先使用 Context 属性而非接口方法
```csharp
[ContextAware]
public partial class MyController
{
public void DoSomething()
{
// ✅ 推荐:使用扩展方法
var model = this.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)