mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-23 19:24:29 +08:00
- 将 Context.GetModel<T>() 调用替换为 this.GetModel<T>() - 将 Context.GetSystem<T>() 调用替换为 this.GetSystem<T>() - 将 Context.GetUtility<T>() 调用替换为 this.GetUtility<T>() - 将 Context.SendCommand() 调用替换为 this.SendCommand() - 将 Context.SendQuery() 调用替换为 this.SendQuery() - 将 Context.SendEvent() 调用替换为 this.SendEvent() - 将 Context.RegisterEvent<T>() 调用替换为 this.RegisterEvent<T>()
404 lines
11 KiB
Markdown
404 lines
11 KiB
Markdown
# 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)
|