refactor(docs): 将控制器实现转换为上下文感知模式

- 将 IController 实现改为使用 [ContextAware] 特性和 partial 类
- 移除手动实现的 GetArchitecture 方法
- 将架构交互方法从 this 调用改为 Context 调用
- 添加必要的 using 语句以支持新的架构访问方式
- 更新所有涉及模型、系统、工具和事件的架构访问代码
- 统一架构访问模式以提高代码一致性和可维护性
This commit is contained in:
GeWuYou 2026-03-08 11:03:02 +08:00
parent 5190b7a463
commit 1b9e81bbdb
26 changed files with 18344 additions and 18165 deletions

View File

@ -1,10 +1,38 @@
namespace GFramework.Core.Abstractions.controller; namespace GFramework.Core.Abstractions.controller;
/// <summary> /// <summary>
/// 控制器接口,定义了控制器的基本契约和行为规范 /// 控制器标记接口,用于标识控制器组件
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 该接口为框架中的控制器组件提供统一的抽象定义, /// <para>
/// 用于实现控制器的标准功能和生命周期管理 /// IController 是一个标记接口Marker Interface不包含任何方法或属性。
/// </remarks> /// 它的作用是标识一个类是控制器,用于协调 Model、System 和 UI 之间的交互。
/// </para>
/// <para>
/// <strong>架构访问</strong>:控制器通常需要访问架构上下文。使用 [ContextAware] 特性
/// 自动生成上下文访问能力:
/// </para>
/// <code>
/// using GFramework.SourceGenerators.Abstractions.rule;
///
/// [ContextAware]
/// public partial class PlayerController : IController
/// {
/// public void Initialize()
/// {
/// // Context 属性由 [ContextAware] 自动生成
/// var playerModel = Context.GetModel&lt;PlayerModel&gt;();
/// var gameSystem = Context.GetSystem&lt;GameSystem&gt;();
/// }
/// }
/// </code>
/// <para>
/// <strong>注意</strong>
/// </para>
/// <list type="bullet">
/// <item>必须添加 partial 关键字</item>
/// <item>[ContextAware] 特性会自动实现 IContextAware 接口</item>
/// <item>Context 属性提供架构上下文访问</item>
/// </list>
/// </remarks>
public interface IController; public interface IController;

File diff suppressed because it is too large Load Diff

View File

@ -1,483 +1,491 @@
# 最佳实践 # 最佳实践
本文档总结了使用 GFramework 的最佳实践和设计模式。 本文档总结了使用 GFramework 的最佳实践和设计模式。
## 架构设计 ## 架构设计
### 1. 清晰的职责分离 ### 1. 清晰的职责分离
**原则**:每一层都有明确的职责,不要混淆。 **原则**:每一层都有明确的职责,不要混淆。
```csharp ```csharp
// ✅ 正确的职责分离 // ✅ 正确的职责分离
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// Model只存储数据 // Model只存储数据
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Score { get; } = new(0); public BindableProperty<int> Score { get; } = new(0);
} }
public class CombatSystem : AbstractSystem public class CombatSystem : AbstractSystem
{ {
// System处理业务逻辑 // System处理业务逻辑
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<AttackEvent>(OnAttack); this.RegisterEvent<AttackEvent>(OnAttack);
} }
private void OnAttack(AttackEvent e) private void OnAttack(AttackEvent e)
{ {
var player = this.GetModel<PlayerModel>(); var player = this.GetModel<PlayerModel>();
player.Health.Value -= e.Damage; player.Health.Value -= e.Damage;
} }
} }
public class PlayerController : IController using GFramework.Core.Abstractions.controller;
{ using GFramework.SourceGenerators.Abstractions.rule;
// Controller连接 UI 和逻辑
public void Initialize() [ContextAware]
{ public partial class PlayerController : IController
var player = _architecture.GetModel<PlayerModel>(); {
player.Health.RegisterWithInitValue(OnHealthChanged); // Controller连接 UI 和逻辑
} public void Initialize()
{
private void OnHealthChanged(int health) var player = Context.GetModel<PlayerModel>();
{ player.Health.RegisterWithInitValue(OnHealthChanged);
UpdateHealthDisplay(health); }
}
} private void OnHealthChanged(int health)
``` {
UpdateHealthDisplay(health);
### 2. 事件驱动设计 }
}
**原则**:使用事件解耦组件,避免直接调用。 ```
```csharp ### 2. 事件驱动设计
// ❌ 紧耦合
public class SystemA : AbstractSystem **原则**:使用事件解耦组件,避免直接调用。
{
private void OnEvent(EventA e) ```csharp
{ // ❌ 紧耦合
var systemB = this.GetSystem<SystemB>(); public class SystemA : AbstractSystem
systemB.DoSomething(); // 直接调用 {
} private void OnEvent(EventA e)
} {
var systemB = this.GetSystem<SystemB>();
// ✅ 松耦合 systemB.DoSomething(); // 直接调用
public class SystemA : AbstractSystem }
{ }
private void OnEvent(EventA e)
{ // ✅ 松耦合
this.SendEvent(new EventB()); // 发送事件 public class SystemA : AbstractSystem
} {
} private void OnEvent(EventA e)
{
public class SystemB : AbstractSystem this.SendEvent(new EventB()); // 发送事件
{ }
protected override void OnInit() }
{
this.RegisterEvent<EventB>(OnEventB); public class SystemB : AbstractSystem
} {
} protected override void OnInit()
``` {
this.RegisterEvent<EventB>(OnEventB);
### 3. 命令查询分离 }
}
**原则**明确区分修改状态Command和查询状态Query ```
```csharp ### 3. 命令查询分离
// ✅ 正确的 CQRS
public class MovePlayerCommand : AbstractCommand **原则**明确区分修改状态Command和查询状态Query
{
public Vector2 Direction { get; set; } ```csharp
// ✅ 正确的 CQRS
protected override void OnDo() public class MovePlayerCommand : AbstractCommand
{ {
// 修改状态 public Vector2 Direction { get; set; }
this.SendEvent(new PlayerMovedEvent { Direction = Direction });
} protected override void OnDo()
} {
// 修改状态
public class GetPlayerPositionQuery : AbstractQuery<Vector2> this.SendEvent(new PlayerMovedEvent { Direction = Direction });
{ }
protected override Vector2 OnDo() }
{
// 只查询,不修改 public class GetPlayerPositionQuery : AbstractQuery<Vector2>
return this.GetModel<PlayerModel>().Position.Value; {
} protected override Vector2 OnDo()
} {
``` // 只查询,不修改
return this.GetModel<PlayerModel>().Position.Value;
## 代码组织 }
}
### 1. 项目结构 ```
``` ## 代码组织
GameProject/
├── Models/ ### 1. 项目结构
│ ├── PlayerModel.cs
│ ├── GameStateModel.cs ```
│ └── InventoryModel.cs GameProject/
├── Systems/ ├── Models/
│ ├── CombatSystem.cs │ ├── PlayerModel.cs
│ ├── InventorySystem.cs │ ├── GameStateModel.cs
│ └── GameLogicSystem.cs │ └── InventoryModel.cs
├── Commands/ ├── Systems/
│ ├── AttackCommand.cs │ ├── CombatSystem.cs
│ ├── MoveCommand.cs │ ├── InventorySystem.cs
│ └── UseItemCommand.cs │ └── GameLogicSystem.cs
├── Queries/ ├── Commands/
│ ├── GetPlayerHealthQuery.cs │ ├── AttackCommand.cs
│ └── GetInventoryItemsQuery.cs │ ├── MoveCommand.cs
├── Events/ │ └── UseItemCommand.cs
│ ├── PlayerDiedEvent.cs ├── Queries/
│ ├── ItemUsedEvent.cs │ ├── GetPlayerHealthQuery.cs
│ └── EnemyDamagedEvent.cs │ └── GetInventoryItemsQuery.cs
├── Controllers/ ├── Events/
│ ├── PlayerController.cs │ ├── PlayerDiedEvent.cs
│ └── UIController.cs │ ├── ItemUsedEvent.cs
├── Utilities/ │ └── EnemyDamagedEvent.cs
│ ├── StorageUtility.cs ├── Controllers/
│ └── MathUtility.cs │ ├── PlayerController.cs
└── GameArchitecture.cs │ └── UIController.cs
``` ├── Utilities/
│ ├── StorageUtility.cs
### 2. 命名规范 │ └── MathUtility.cs
└── GameArchitecture.cs
```csharp ```
// Models使用 Model 后缀
public class PlayerModel : AbstractModel { } ### 2. 命名规范
public class GameStateModel : AbstractModel { }
```csharp
// Systems使用 System 后缀 // Models使用 Model 后缀
public class CombatSystem : AbstractSystem { } public class PlayerModel : AbstractModel { }
public class InventorySystem : AbstractSystem { } public class GameStateModel : AbstractModel { }
// Commands使用 Command 后缀 // Systems使用 System 后缀
public class AttackCommand : AbstractCommand { } public class CombatSystem : AbstractSystem { }
public class MoveCommand : AbstractCommand { } public class InventorySystem : AbstractSystem { }
// Queries使用 Query 后缀 // Commands使用 Command 后缀
public class GetPlayerHealthQuery : AbstractQuery<int> { } public class AttackCommand : AbstractCommand { }
public class GetInventoryItemsQuery : AbstractQuery<List<Item>> { } public class MoveCommand : AbstractCommand { }
// Events使用 Event 后缀 // Queries使用 Query 后缀
public class PlayerDiedEvent : IEvent { } public class GetPlayerHealthQuery : AbstractQuery<int> { }
public class ItemUsedEvent : IEvent { } public class GetInventoryItemsQuery : AbstractQuery<List<Item>> { }
// Controllers使用 Controller 后缀 // Events使用 Event 后缀
public class PlayerController : IController { } public class PlayerDiedEvent : IEvent { }
public class ItemUsedEvent : IEvent { }
// Utilities使用 Utility 后缀
public class StorageUtility : IUtility { } // Controllers使用 Controller 后缀
``` public class PlayerController : IController { }
## 内存管理 // Utilities使用 Utility 后缀
public class StorageUtility : IUtility { }
### 1. 正确的注销管理 ```
```csharp ## 内存管理
public class MyController : IController
{ ### 1. 正确的注销管理
private IUnRegisterList _unregisterList = new UnRegisterList();
```csharp
public void Initialize() using GFramework.Core.Abstractions.controller;
{ using GFramework.SourceGenerators.Abstractions.rule;
var model = _architecture.GetModel<PlayerModel>();
[ContextAware]
// 注册事件并添加到注销列表 public partial class MyController : IController
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied) {
.AddToUnregisterList(_unregisterList); private IUnRegisterList _unregisterList = new UnRegisterList();
// 注册属性监听并添加到注销列表 public void Initialize()
model.Health.Register(OnHealthChanged) {
.AddToUnregisterList(_unregisterList); var model = Context.GetModel<PlayerModel>();
}
// 注册事件并添加到注销列表
public void Cleanup() Context.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
{ .AddToUnregisterList(_unregisterList);
// 统一注销所有监听器
_unregisterList.UnRegisterAll(); // 注册属性监听并添加到注销列表
} model.Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
private void OnPlayerDied(PlayerDiedEvent e) { } }
private void OnHealthChanged(int health) { }
} public void Cleanup()
``` {
// 统一注销所有监听器
### 2. 生命周期管理 _unregisterList.UnRegisterAll();
}
```csharp
public class GameManager private void OnPlayerDied(PlayerDiedEvent e) { }
{ private void OnHealthChanged(int health) { }
private GameArchitecture _architecture; }
```
public void StartGame()
{ ### 2. 生命周期管理
_architecture = new GameArchitecture();
_architecture.Initialize(); ```csharp
} public class GameManager
{
public void EndGame() private GameArchitecture _architecture;
{
// 销毁架构,自动清理所有组件 public void StartGame()
_architecture.Destroy(); {
_architecture = null; _architecture = new GameArchitecture();
} _architecture.Initialize();
} }
```
public void EndGame()
## 性能优化 {
// 销毁架构,自动清理所有组件
### 1. 缓存组件引用 _architecture.Destroy();
_architecture = null;
```csharp }
// ❌ 低效:每次都查询 }
public void Update() ```
{
var model = _architecture.GetModel<PlayerModel>(); ## 性能优化
model.Health.Value -= 1;
} ### 1. 缓存组件引用
// ✅ 高效:缓存引用 ```csharp
private PlayerModel _playerModel; // ❌ 低效:每次都查询
public void Update()
public void Initialize() {
{ var model = Context.GetModel<PlayerModel>();
_playerModel = _architecture.GetModel<PlayerModel>(); model.Health.Value -= 1;
} }
public void Update() // ✅ 高效:缓存引用
{ private PlayerModel _playerModel;
_playerModel.Health.Value -= 1;
} public void Initialize()
``` {
_playerModel = Context.GetModel<PlayerModel>();
### 2. 避免频繁的事件创建 }
```csharp public void Update()
// ❌ 低效:每帧创建新事件 {
public void Update() _playerModel.Health.Value -= 1;
{ }
this.SendEvent(new UpdateEvent()); // 频繁分配内存 ```
}
### 2. 避免频繁的事件创建
// ✅ 高效:复用事件或使用对象池
private UpdateEvent _updateEvent = new UpdateEvent(); ```csharp
// ❌ 低效:每帧创建新事件
public void Update() public void Update()
{ {
this.SendEvent(_updateEvent); Context.SendEvent(new UpdateEvent()); // 频繁分配内存
} }
```
// ✅ 高效:复用事件或使用对象池
### 3. 异步处理重操作 private UpdateEvent _updateEvent = new UpdateEvent();
```csharp public void Update()
public class LoadDataCommand : AbstractCommand {
{ Context.SendEvent(_updateEvent);
protected override async void OnDo() }
{ ```
// 异步加载数据,不阻塞主线程
var data = await LoadDataAsync(); ### 3. 异步处理重操作
this.SendEvent(new DataLoadedEvent { Data = data });
} ```csharp
public class LoadDataCommand : AbstractCommand
private async Task<Data> LoadDataAsync() {
{ protected override async void OnDo()
return await Task.Run(() => {
{ // 异步加载数据,不阻塞主线程
// 耗时操作 var data = await LoadDataAsync();
return new Data(); this.SendEvent(new DataLoadedEvent { Data = data });
}); }
}
} private async Task<Data> LoadDataAsync()
``` {
return await Task.Run(() =>
## 测试 {
// 耗时操作
### 1. 单元测试 return new Data();
});
```csharp }
[TestFixture] }
public class CombatSystemTests ```
{
private GameArchitecture _architecture; ## 测试
private PlayerModel _playerModel;
### 1. 单元测试
[SetUp]
public void Setup() ```csharp
{ [TestFixture]
_architecture = new TestArchitecture(); public class CombatSystemTests
_architecture.Initialize(); {
_playerModel = _architecture.GetModel<PlayerModel>(); private GameArchitecture _architecture;
} private PlayerModel _playerModel;
[TearDown] [SetUp]
public void Teardown() public void Setup()
{ {
_architecture.Destroy(); _architecture = new TestArchitecture();
} _architecture.Initialize();
_playerModel = _architecture.GetModel<PlayerModel>();
[Test] }
public void PlayerTakeDamage_ReducesHealth()
{ [TearDown]
_playerModel.Health.Value = 100; public void Teardown()
_architecture.SendEvent(new DamageEvent { Amount = 10 }); {
Assert.AreEqual(90, _playerModel.Health.Value); _architecture.Destroy();
} }
[Test] [Test]
public void PlayerDies_WhenHealthReachesZero() public void PlayerTakeDamage_ReducesHealth()
{ {
_playerModel.Health.Value = 10; _playerModel.Health.Value = 100;
_architecture.SendEvent(new DamageEvent { Amount = 10 }); _architecture.SendEvent(new DamageEvent { Amount = 10 });
Assert.AreEqual(0, _playerModel.Health.Value); Assert.AreEqual(90, _playerModel.Health.Value);
} }
}
``` [Test]
public void PlayerDies_WhenHealthReachesZero()
### 2. 集成测试 {
_playerModel.Health.Value = 10;
```csharp _architecture.SendEvent(new DamageEvent { Amount = 10 });
[TestFixture] Assert.AreEqual(0, _playerModel.Health.Value);
public class GameFlowTests }
{ }
private GameArchitecture _architecture; ```
[SetUp] ### 2. 集成测试
public void Setup()
{ ```csharp
_architecture = new GameArchitecture(); [TestFixture]
_architecture.Initialize(); public class GameFlowTests
} {
private GameArchitecture _architecture;
[Test]
public void CompleteGameFlow() [SetUp]
{ public void Setup()
// 初始化 {
var player = _architecture.GetModel<PlayerModel>(); _architecture = new GameArchitecture();
Assert.AreEqual(100, player.Health.Value); _architecture.Initialize();
}
// 执行操作
_architecture.SendCommand(new AttackCommand { Damage = 20 }); [Test]
public void CompleteGameFlow()
// 验证结果 {
Assert.AreEqual(80, player.Health.Value); // 初始化
} var player = _architecture.GetModel<PlayerModel>();
} Assert.AreEqual(100, player.Health.Value);
```
// 执行操作
## 文档 _architecture.SendCommand(new AttackCommand { Damage = 20 });
### 1. 代码注释 // 验证结果
Assert.AreEqual(80, player.Health.Value);
```csharp }
/// <summary> }
/// 玩家模型,存储玩家的所有状态数据 ```
/// </summary>
public class PlayerModel : AbstractModel ## 文档
{
/// <summary> ### 1. 代码注释
/// 玩家的生命值,使用 BindableProperty 实现响应式更新
/// </summary> ```csharp
public BindableProperty<int> Health { get; } = new(100); /// <summary>
/// 玩家模型,存储玩家的所有状态数据
protected override void OnInit() /// </summary>
{ public class PlayerModel : AbstractModel
// 监听生命值变化,当生命值为 0 时发送死亡事件 {
Health.Register(hp => /// <summary>
{ /// 玩家的生命值,使用 BindableProperty 实现响应式更新
if (hp <= 0) /// </summary>
this.SendEvent(new PlayerDiedEvent()); public BindableProperty<int> Health { get; } = new(100);
});
} protected override void OnInit()
} {
``` // 监听生命值变化,当生命值为 0 时发送死亡事件
Health.Register(hp =>
### 2. 架构文档 {
if (hp <= 0)
为你的项目编写架构文档,说明: this.SendEvent(new PlayerDiedEvent());
});
- 主要的 Model、System、Command、Query }
- 关键事件流 }
- 组件间的通信方式 ```
- 扩展点和插件机制
### 2. 架构文档
## 常见陷阱
为你的项目编写架构文档,说明:
### 1. 在 Model 中包含业务逻辑
- 主要的 Model、System、Command、Query
```csharp - 关键事件流
// ❌ 错误 - 组件间的通信方式
public class PlayerModel : AbstractModel - 扩展点和插件机制
{
public void TakeDamage(int damage) ## 常见陷阱
{
Health.Value -= damage; ### 1. 在 Model 中包含业务逻辑
if (Health.Value <= 0)
Die(); ```csharp
} // ❌ 错误
} public class PlayerModel : AbstractModel
{
// ✅ 正确 public void TakeDamage(int damage)
public class CombatSystem : AbstractSystem {
{ Health.Value -= damage;
private void OnDamage(DamageEvent e) if (Health.Value <= 0)
{ Die();
var player = this.GetModel<PlayerModel>(); }
player.Health.Value -= e.Amount; }
}
} // ✅ 正确
``` public class CombatSystem : AbstractSystem
{
### 2. 忘记注销监听器 private void OnDamage(DamageEvent e)
{
```csharp var player = this.GetModel<PlayerModel>();
// ❌ 错误:可能导致内存泄漏 player.Health.Value -= e.Amount;
public void Initialize() }
{ }
this.RegisterEvent<Event1>(OnEvent1); // 未注销 ```
}
### 2. 忘记注销监听器
// ✅ 正确
private IUnRegisterList _unregisterList = new UnRegisterList(); ```csharp
// ❌ 错误:可能导致内存泄漏
public void Initialize() public void Initialize()
{ {
this.RegisterEvent<Event1>(OnEvent1) Context.RegisterEvent<Event1>(OnEvent1); // 未注销
.AddToUnregisterList(_unregisterList); }
}
// ✅ 正确
public void Cleanup() private IUnRegisterList _unregisterList = new UnRegisterList();
{
_unregisterList.UnRegisterAll(); public void Initialize()
} {
``` Context.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
### 3. 直接调用其他系统 }
```csharp public void Cleanup()
// ❌ 错误:紧耦合 {
public class SystemA : AbstractSystem _unregisterList.UnRegisterAll();
{ }
private void OnEvent(EventA e) ```
{
var systemB = this.GetSystem<SystemB>(); ### 3. 直接调用其他系统
systemB.DoSomething();
} ```csharp
} // ❌ 错误:紧耦合
public class SystemA : AbstractSystem
// ✅ 正确:使用事件解耦 {
public class SystemA : AbstractSystem private void OnEvent(EventA e)
{ {
private void OnEvent(EventA e) var systemB = this.GetSystem<SystemB>();
{ systemB.DoSomething();
this.SendEvent(new EventB()); }
} }
}
``` // ✅ 正确:使用事件解耦
public class SystemA : AbstractSystem
--- {
private void OnEvent(EventA e)
遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。 {
this.SendEvent(new EventB());
}
}
```
---
遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,447 +1,451 @@
# Command 包使用说明 # Command 包使用说明
## 概述 ## 概述
Command 包实现了命令模式Command Pattern用于封装用户操作和业务逻辑。通过命令模式可以将请求封装为对象实现操作的参数化、队列化、日志记录、撤销等功能。 Command 包实现了命令模式Command Pattern用于封装用户操作和业务逻辑。通过命令模式可以将请求封装为对象实现操作的参数化、队列化、日志记录、撤销等功能。
命令系统是 GFramework CQRS 架构的重要组成部分,与事件系统和查询系统协同工作,实现完整的业务逻辑处理流程。 命令系统是 GFramework CQRS 架构的重要组成部分,与事件系统和查询系统协同工作,实现完整的业务逻辑处理流程。
## 核心接口 ## 核心接口
### ICommand ### ICommand
无返回值命令接口,定义了命令的基本契约。 无返回值命令接口,定义了命令的基本契约。
**核心方法:** **核心方法:**
```csharp ```csharp
void Execute(); // 执行命令 void Execute(); // 执行命令
``` ```
### ICommand`<TResult>` ### ICommand`<TResult>`
带返回值的命令接口,用于需要返回执行结果的命令。 带返回值的命令接口,用于需要返回执行结果的命令。
**核心方法:** **核心方法:**
```csharp ```csharp
TResult Execute(); // 执行命令并返回结果 TResult Execute(); // 执行命令并返回结果
``` ```
## 核心类 ## 核心类
### AbstractCommand ### AbstractCommand
无返回值命令的抽象基类,提供了命令的基础实现。它继承自 ContextAwareBase具有上下文感知能力。 无返回值命令的抽象基类,提供了命令的基础实现。它继承自 ContextAwareBase具有上下文感知能力。
**核心方法:** **核心方法:**
```csharp ```csharp
void ICommand.Execute(); // 实现 ICommand 接口 void ICommand.Execute(); // 实现 ICommand 接口
protected abstract void OnExecute(); // 抽象执行方法,由子类实现 protected abstract void OnExecute(); // 抽象执行方法,由子类实现
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 定义一个无返回值的基础命令 // 定义一个无返回值的基础命令
public class SimpleCommand : AbstractCommand public class SimpleCommand : AbstractCommand
{ {
protected override void OnExecute() protected override void OnExecute()
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
playerModel.Health.Value = playerModel.MaxHealth.Value; playerModel.Health.Value = playerModel.MaxHealth.Value;
this.SendEvent(new PlayerHealthRestoredEvent()); this.SendEvent(new PlayerHealthRestoredEvent());
} }
} }
// 使用命令 // 使用命令
public class GameController : IController using GFramework.Core.Abstractions.controller;
{ using GFramework.SourceGenerators.Abstractions.rule;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
[ContextAware]
public void OnRestoreHealthButtonClicked() public partial class GameController : IController
{ {
this.SendCommand(new SimpleCommand()); public void OnRestoreHealthButtonClicked()
} {
} Context.SendCommand(new SimpleCommand());
``` }
}
### AbstractCommand`<TResult>` ```
无输入参数但带返回值的命令基类。 ### AbstractCommand`<TResult>`
**核心方法:** 无输入参数但带返回值的命令基类。
```csharp **核心方法:**
TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
protected abstract TResult OnExecute(); // 抽象执行方法,由子类实现 ```csharp
``` TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
protected abstract TResult OnExecute(); // 抽象执行方法,由子类实现
**使用示例:** ```
```csharp **使用示例:**
// 定义一个无输入但有返回值的命令
public class GetPlayerHealthQuery : AbstractCommand<int> ```csharp
{ // 定义一个无输入但有返回值的命令
protected override int OnExecute() public class GetPlayerHealthQuery : AbstractCommand<int>
{ {
var playerModel = this.GetModel<PlayerModel>(); protected override int OnExecute()
return playerModel.Health.Value; {
} var playerModel = this.GetModel<PlayerModel>();
} return playerModel.Health.Value;
}
// 使用命令 }
public class UISystem : AbstractSystem
{ // 使用命令
protected override void OnInit() public class UISystem : AbstractSystem
{ {
this.RegisterEvent<UpdateUIEvent>(OnUpdateUI); protected override void OnInit()
} {
this.RegisterEvent<UpdateUIEvent>(OnUpdateUI);
private void OnUpdateUI(UpdateUIEvent e) }
{
var health = this.SendCommand(new GetPlayerHealthQuery()); private void OnUpdateUI(UpdateUIEvent e)
Console.WriteLine($"Player health: {health}"); {
} var health = this.SendCommand(new GetPlayerHealthQuery());
} Console.WriteLine($"Player health: {health}");
``` }
}
## 命令的生命周期 ```
1. **创建命令**:实例化命令对象,传入必要的参数 ## 命令的生命周期
2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
3. **返回结果**:对于带返回值的命令,返回执行结果 1. **创建命令**:实例化命令对象,传入必要的参数
4. **命令销毁**:命令执行完毕后可以被垃圾回收 2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
3. **返回结果**:对于带返回值的命令,返回执行结果
**注意事项:** 4. **命令销毁**:命令执行完毕后可以被垃圾回收
- 命令应该是无状态的,执行完即可丢弃 **注意事项:**
- 避免在命令中保存长期引用
- 命令执行应该是原子操作 - 命令应该是无状态的,执行完即可丢弃
- 避免在命令中保存长期引用
## CommandBus - 命令总线 - 命令执行应该是原子操作
### 功能说明 ## CommandBus - 命令总线
`CommandBus` 是命令执行的核心组件,负责发送和执行命令。 ### 功能说明
**主要方法:** `CommandBus` 是命令执行的核心组件,负责发送和执行命令。
```csharp **主要方法:**
void Send(ICommand command); // 发送无返回值命令
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令 ```csharp
``` void Send(ICommand command); // 发送无返回值命令
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令
**特点:** ```
- 统一的命令执行入口 **特点:**
- 支持同步命令执行
- 与架构上下文集成 - 统一的命令执行入口
- 支持同步命令执行
### 使用示例 - 与架构上下文集成
```csharp ### 使用示例
// 通过架构获取命令总线
var commandBus = architecture.Context.CommandBus; ```csharp
// 通过架构获取命令总线
// 发送无返回值命令 var commandBus = architecture.Context.CommandBus;
commandBus.Send(new StartGameCommand(1, "Player1"));
// 发送无返回值命令
// 发送带返回值命令 commandBus.Send(new StartGameCommand(1, "Player1"));
var damage = commandBus.Send(new CalculateDamageCommand(100, 50));
``` // 发送带返回值命令
var damage = commandBus.Send(new CalculateDamageCommand(100, 50));
## 命令基类变体 ```
框架提供了多种命令基类以满足不同需求: ## 命令基类变体
### AbstractCommand`<TInput>` 框架提供了多种命令基类以满足不同需求:
带输入参数的无返回值命令类。通过 `ICommandInput` 接口传递参数。 ### AbstractCommand`<TInput>`
**核心方法:** 带输入参数的无返回值命令类。通过 `ICommandInput` 接口传递参数。
```csharp **核心方法:**
void ICommand.Execute(); // 实现 ICommand 接口
protected abstract void OnExecute(TInput input); // 抽象执行方法,接收输入参数 ```csharp
``` void ICommand.Execute(); // 实现 ICommand 接口
protected abstract void OnExecute(TInput input); // 抽象执行方法,接收输入参数
**使用示例:** ```
```csharp **使用示例:**
// 定义输入对象
public class StartGameInput : ICommandInput ```csharp
{ // 定义输入对象
public int LevelId { get; set; } public class StartGameInput : ICommandInput
public string PlayerName { get; set; } {
} public int LevelId { get; set; }
public string PlayerName { get; set; }
// 定义命令 }
public class StartGameCommand : AbstractCommand<StartGameInput>
{ // 定义命令
protected override void OnExecute(StartGameInput input) public class StartGameCommand : AbstractCommand<StartGameInput>
{ {
var playerModel = this.GetModel<PlayerModel>(); protected override void OnExecute(StartGameInput input)
var gameModel = this.GetModel<GameModel>(); {
var playerModel = this.GetModel<PlayerModel>();
playerModel.PlayerName.Value = input.PlayerName; var gameModel = this.GetModel<GameModel>();
gameModel.CurrentLevel.Value = input.LevelId;
gameModel.GameState.Value = GameState.Playing; playerModel.PlayerName.Value = input.PlayerName;
gameModel.CurrentLevel.Value = input.LevelId;
this.SendEvent(new GameStartedEvent()); gameModel.GameState.Value = GameState.Playing;
}
} this.SendEvent(new GameStartedEvent());
}
// 使用命令 }
public class GameController : IController
{ // 使用命令
public IArchitecture GetArchitecture() => GameArchitecture.Interface; using GFramework.Core.Abstractions.controller;
using GFramework.SourceGenerators.Abstractions.rule;
public void OnStartButtonClicked()
{ [ContextAware]
var input = new StartGameInput { LevelId = 1, PlayerName = "Player1" }; public partial class GameController : IController
this.SendCommand(new StartGameCommand { Input = input }); {
} public void OnStartButtonClicked()
} {
``` var input = new StartGameInput { LevelId = 1, PlayerName = "Player1" };
Context.SendCommand(new StartGameCommand { Input = input });
### AbstractCommand`<TInput, TResult>` }
}
既带输入参数又带返回值的命令类。 ```
**核心方法:** ### AbstractCommand`<TInput, TResult>`
```csharp 既带输入参数又带返回值的命令类。
TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
protected abstract TResult OnExecute(TInput input); // 抽象执行方法,接收输入参数 **核心方法:**
```
```csharp
**使用示例:** TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
protected abstract TResult OnExecute(TInput input); // 抽象执行方法,接收输入参数
```csharp ```
// 定义输入对象
public class CalculateDamageInput : ICommandInput **使用示例:**
{
public int AttackerAttackPower { get; set; } ```csharp
public int DefenderDefense { get; set; } // 定义输入对象
} public class CalculateDamageInput : ICommandInput
{
// 定义命令 public int AttackerAttackPower { get; set; }
public class CalculateDamageCommand : AbstractCommand<CalculateDamageInput, int> public int DefenderDefense { get; set; }
{ }
protected override int OnExecute(CalculateDamageInput input)
{ // 定义命令
var config = this.GetModel<GameConfigModel>(); public class CalculateDamageCommand : AbstractCommand<CalculateDamageInput, int>
var baseDamage = input.AttackerAttackPower - input.DefenderDefense; {
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier); protected override int OnExecute(CalculateDamageInput input)
return (int)finalDamage; {
} var config = this.GetModel<GameConfigModel>();
} var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
// 使用命令 return (int)finalDamage;
public class CombatSystem : AbstractSystem }
{ }
protected override void OnInit() { }
// 使用命令
public void Attack(Character attacker, Character defender) public class CombatSystem : AbstractSystem
{ {
var input = new CalculateDamageInput protected override void OnInit() { }
{
AttackerAttackPower = attacker.AttackPower, public void Attack(Character attacker, Character defender)
DefenderDefense = defender.Defense {
}; var input = new CalculateDamageInput
{
var damage = this.SendCommand(new CalculateDamageCommand { Input = input }); AttackerAttackPower = attacker.AttackPower,
defender.Health -= damage; DefenderDefense = defender.Defense
this.SendEvent(new DamageDealtEvent(attacker, defender, damage)); };
}
} var damage = this.SendCommand(new CalculateDamageCommand { Input = input });
``` defender.Health -= damage;
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
### AbstractAsyncCommand`<TInput>` }
}
支持异步执行的带输入参数的无返回值命令基类。 ```
**核心方法:** ### AbstractAsyncCommand`<TInput>`
```csharp 支持异步执行的带输入参数的无返回值命令基类。
Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口
protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法 **核心方法:**
```
```csharp
### AbstractAsyncCommand`<TInput, TResult>` Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口
protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法
支持异步执行的既带输入参数又带返回值的命令基类。 ```
**核心方法:** ### AbstractAsyncCommand`<TInput, TResult>`
```csharp 支持异步执行的既带输入参数又带返回值的命令基类。
Task<TResult> IAsyncCommand<TResult>.ExecuteAsync(); // 实现异步命令接口
protected abstract Task<TResult> OnExecuteAsync(TInput input); // 抽象异步执行方法 **核心方法:**
```
```csharp
**使用示例:** Task<TResult> IAsyncCommand<TResult>.ExecuteAsync(); // 实现异步命令接口
protected abstract Task<TResult> OnExecuteAsync(TInput input); // 抽象异步执行方法
```csharp ```
// 定义输入对象
public class LoadSaveDataInput : ICommandInput **使用示例:**
{
public string SaveSlot { get; set; } ```csharp
} // 定义输入对象
public class LoadSaveDataInput : ICommandInput
// 定义异步命令 {
public class LoadSaveDataCommand : AbstractAsyncCommand<LoadSaveDataInput, SaveData> public string SaveSlot { get; set; }
{ }
protected override async Task<SaveData> OnExecuteAsync(LoadSaveDataInput input)
{ // 定义异步命令
var storage = this.GetUtility<IStorageUtility>(); public class LoadSaveDataCommand : AbstractAsyncCommand<LoadSaveDataInput, SaveData>
return await storage.LoadSaveDataAsync(input.SaveSlot); {
} protected override async Task<SaveData> OnExecuteAsync(LoadSaveDataInput input)
} {
var storage = this.GetUtility<IStorageUtility>();
// 使用异步命令 return await storage.LoadSaveDataAsync(input.SaveSlot);
public class SaveSystem : AbstractSystem }
{ }
protected override void OnInit()
{ // 使用异步命令
this.RegisterEvent<LoadGameRequestEvent>(OnLoadGameRequest); public class SaveSystem : AbstractSystem
} {
protected override void OnInit()
private async void OnLoadGameRequest(LoadGameRequestEvent e) {
{ this.RegisterEvent<LoadGameRequestEvent>(OnLoadGameRequest);
var input = new LoadSaveDataInput { SaveSlot = e.SaveSlot }; }
var saveData = await this.SendCommandAsync(new LoadSaveDataCommand { Input = input });
private async void OnLoadGameRequest(LoadGameRequestEvent e)
if (saveData != null) {
{ var input = new LoadSaveDataInput { SaveSlot = e.SaveSlot };
this.SendEvent(new GameLoadedEvent { SaveData = saveData }); var saveData = await this.SendCommandAsync(new LoadSaveDataCommand { Input = input });
}
} if (saveData != null)
} {
``` this.SendEvent(new GameLoadedEvent { SaveData = saveData });
}
## 命令处理器执行 }
}
所有发送给命令总线的命令最终都会通过 `CommandExecutor` 来执行: ```
```csharp ## 命令处理器执行
public class CommandExecutor
{ 所有发送给命令总线的命令最终都会通过 `CommandExecutor` 来执行:
public static void Execute(ICommand command)
{ ```csharp
command.Execute(); public class CommandExecutor
} {
public static void Execute(ICommand command)
public static TResult Execute<TResult>(ICommand<TResult> command) {
{ command.Execute();
return command.Execute(); }
}
} public static TResult Execute<TResult>(ICommand<TResult> command)
``` {
return command.Execute();
**特点:** }
}
- 提供统一的命令执行机制 ```
- 支持同步和异步命令执行
- 可以扩展添加中间件逻辑 **特点:**
## 使用场景 - 提供统一的命令执行机制
- 支持同步和异步命令执行
### 1. 用户交互操作 - 可以扩展添加中间件逻辑
```csharp ## 使用场景
public class SaveGameCommand : AbstractCommand
{ ### 1. 用户交互操作
private readonly string _saveSlot;
```csharp
public SaveGameCommand(string saveSlot) public class SaveGameCommand : AbstractCommand
{ {
_saveSlot = saveSlot; private readonly string _saveSlot;
}
public SaveGameCommand(string saveSlot)
protected override void OnExecute() {
{ _saveSlot = saveSlot;
var saveSystem = this.GetSystem<SaveSystem>(); }
var playerModel = this.GetModel<PlayerModel>();
protected override void OnExecute()
saveSystem.SavePlayerData(playerModel, _saveSlot); {
this.SendEvent(new GameSavedEvent(_saveSlot)); var saveSystem = this.GetSystem<SaveSystem>();
} var playerModel = this.GetModel<PlayerModel>();
}
``` saveSystem.SavePlayerData(playerModel, _saveSlot);
this.SendEvent(new GameSavedEvent(_saveSlot));
### 2. 业务流程控制 }
}
```csharp ```
public class LoadLevelCommand : AbstractCommand
{ ### 2. 业务流程控制
private readonly int _levelId;
```csharp
public LoadLevelCommand(int levelId) public class LoadLevelCommand : AbstractCommand
{ {
_levelId = levelId; private readonly int _levelId;
}
public LoadLevelCommand(int levelId)
protected override void OnExecute() {
{ _levelId = levelId;
var levelSystem = this.GetSystem<LevelSystem>(); }
var uiSystem = this.GetSystem<UISystem>();
protected override void OnExecute()
// 显示加载界面 {
uiSystem.ShowLoadingScreen(); var levelSystem = this.GetSystem<LevelSystem>();
var uiSystem = this.GetSystem<UISystem>();
// 加载关卡
levelSystem.LoadLevel(_levelId); // 显示加载界面
uiSystem.ShowLoadingScreen();
// 发送事件
this.SendEvent(new LevelLoadedEvent(_levelId)); // 加载关卡
} levelSystem.LoadLevel(_levelId);
}
``` // 发送事件
this.SendEvent(new LevelLoadedEvent(_levelId));
## 最佳实践 }
}
1. **保持命令原子性**:一个命令应该完成一个完整的业务操作 ```
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入 ## 最佳实践
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
5. **合理使用返回值**:只在确实需要返回结果时使用带返回值的命令 1. **保持命令原子性**:一个命令应该完成一个完整的业务操作
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand` 2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
7. **单一职责原则**:每个命令只负责一个特定的业务操作 3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
8. **使用异步命令**:对于需要长时间执行的操作,使用异步命令避免阻塞 4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
9. **命令验证**:在命令执行前验证输入参数的有效性 5. **合理使用返回值**:只在确实需要返回结果时使用带返回值的命令
10. **错误处理**:在命令中适当处理异常情况 6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand`
7. **单一职责原则**:每个命令只负责一个特定的业务操作
## 命令模式优势 8. **使用异步命令**:对于需要长时间执行的操作,使用异步命令避免阻塞
9. **命令验证**:在命令执行前验证输入参数的有效性
### 1. 可扩展性 10. **错误处理**:在命令中适当处理异常情况
- 命令可以被序列化和存储 ## 命令模式优势
- 支持命令队列和批处理
- 便于实现撤销/重做功能 ### 1. 可扩展性
### 2. 可测试性 - 命令可以被序列化和存储
- 支持命令队列和批处理
- 命令逻辑独立,易于单元测试 - 便于实现撤销/重做功能
- 可以模拟命令执行结果
- 支持行为驱动开发 ### 2. 可测试性
### 3. 可维护性 - 命令逻辑独立,易于单元测试
- 可以模拟命令执行结果
- 业务逻辑集中管理 - 支持行为驱动开发
- 降低组件间耦合度
- 便于重构和扩展 ### 3. 可维护性
## 相关包 - 业务逻辑集中管理
- 降低组件间耦合度
- [`architecture`](./architecture.md) - 架构核心,负责命令的分发和执行 - 便于重构和扩展
- [`extensions`](./extensions.md) - 提供 `SendCommand()` 扩展方法
- [`query`](./query.md) - 查询模式,用于数据查询 ## 相关包
- [`events`](./events.md) - 事件系统,命令执行后的通知机制
- [`system`](./system.md) - 业务系统,命令的主要执行者 - [`architecture`](./architecture.md) - 架构核心,负责命令的分发和执行
- [`model`](./model.md) - 数据模型,命令操作的数据 - [`extensions`](./extensions.md) - 提供 `SendCommand()` 扩展方法
- [`query`](./query.md) - 查询模式,用于数据查询
--- - [`events`](./events.md) - 事件系统,命令执行后的通知机制
- [`system`](./system.md) - 业务系统,命令的主要执行者
- [`model`](./model.md) - 数据模型,命令操作的数据
---
**许可证**Apache 2.0 **许可证**Apache 2.0

File diff suppressed because it is too large Load Diff

View File

@ -232,17 +232,18 @@ public class GameArchitecture : Architecture
using Arch.Core; using Arch.Core;
using GFramework.Core.Abstractions.controller; using GFramework.Core.Abstractions.controller;
using MyGame.Components; using MyGame.Components;
using GFramework.Core.Abstractions.controller;
using GFramework.SourceGenerators.Abstractions.rule;
public class GameController : IController [ContextAware]
public partial class GameController : IController
{ {
private World _world; private World _world;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void Start() public void Start()
{ {
// 获取 World // 获取 World
_world = this.GetService<World>(); _world = Context.GetService<World>();
// 创建玩家实体 // 创建玩家实体
var player = _world.Create( var player = _world.Create(

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,427 +1,434 @@
# Property 包使用说明 # Property 包使用说明
## 概述 ## 概述
Property 包提供了可绑定属性BindableProperty的实现支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。 Property 包提供了可绑定属性BindableProperty的实现支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。
BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。 BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。
## 核心接口 ## 核心接口
### IReadonlyBindableProperty`<T>` ### IReadonlyBindableProperty`<T>`
只读可绑定属性接口,提供属性值的读取和变更监听功能。 只读可绑定属性接口,提供属性值的读取和变更监听功能。
**核心成员:** **核心成员:**
```csharp ```csharp
// 获取属性值 // 获取属性值
T Value { get; } T Value { get; }
// 注册监听(不立即触发回调) // 注册监听(不立即触发回调)
IUnRegister Register(Action<T> onValueChanged); IUnRegister Register(Action<T> onValueChanged);
// 注册监听并立即触发回调传递当前值 // 注册监听并立即触发回调传递当前值
IUnRegister RegisterWithInitValue(Action<T> action); IUnRegister RegisterWithInitValue(Action<T> action);
// 取消监听 // 取消监听
void UnRegister(Action<T> onValueChanged); void UnRegister(Action<T> onValueChanged);
``` ```
### IBindableProperty`<T>` ### IBindableProperty`<T>`
可绑定属性接口,继承自只读接口,增加了修改能力。 可绑定属性接口,继承自只读接口,增加了修改能力。
**核心成员:** **核心成员:**
```csharp ```csharp
// 可读写的属性值 // 可读写的属性值
new T Value { get; set; } new T Value { get; set; }
// 设置值但不触发事件 // 设置值但不触发事件
void SetValueWithoutEvent(T newValue); void SetValueWithoutEvent(T newValue);
``` ```
## 核心类 ## 核心类
### BindableProperty`<T>` ### BindableProperty`<T>`
可绑定属性的完整实现。 可绑定属性的完整实现。
**核心方法:** **核心方法:**
```csharp ```csharp
// 构造函数 // 构造函数
BindableProperty(T defaultValue = default!); BindableProperty(T defaultValue = default!);
// 属性值 // 属性值
T Value { get; set; } T Value { get; set; }
// 注册监听 // 注册监听
IUnRegister Register(Action<T> onValueChanged); IUnRegister Register(Action<T> onValueChanged);
IUnRegister RegisterWithInitValue(Action<T> action); IUnRegister RegisterWithInitValue(Action<T> action);
// 取消监听 // 取消监听
void UnRegister(Action<T> onValueChanged); void UnRegister(Action<T> onValueChanged);
// 设置值但不触发事件 // 设置值但不触发事件
void SetValueWithoutEvent(T newValue); void SetValueWithoutEvent(T newValue);
// 设置自定义比较器 // 设置自定义比较器
BindableProperty<T> WithComparer(Func<T, T, bool> comparer); BindableProperty<T> WithComparer(Func<T, T, bool> comparer);
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 创建可绑定属性 // 创建可绑定属性
var health = new BindableProperty<int>(100); var health = new BindableProperty<int>(100);
// 监听值变化(不会立即触发) // 监听值变化(不会立即触发)
var unregister = health.Register(newValue => var unregister = health.Register(newValue =>
{ {
Console.WriteLine($"Health changed to: {newValue}"); Console.WriteLine($"Health changed to: {newValue}");
}); });
// 设置值(会触发监听器) // 设置值(会触发监听器)
health.Value = 50; // 输出: Health changed to: 50 health.Value = 50; // 输出: Health changed to: 50
// 取消监听 // 取消监听
unregister.UnRegister(); unregister.UnRegister();
// 设置值但不触发事件 // 设置值但不触发事件
health.SetValueWithoutEvent(75); health.SetValueWithoutEvent(75);
``` ```
**高级功能:** **高级功能:**
```csharp ```csharp
// 1. 注册并立即获得当前值 // 1. 注册并立即获得当前值
health.RegisterWithInitValue(value => health.RegisterWithInitValue(value =>
{ {
Console.WriteLine($"Current health: {value}"); // 立即输出当前值 Console.WriteLine($"Current health: {value}"); // 立即输出当前值
// 后续值变化时也会调用 // 后续值变化时也会调用
}); });
// 2. 自定义比较器(静态方法) // 2. 自定义比较器(静态方法)
BindableProperty<int>.Comparer = (a, b) => Math.Abs(a - b) < 1; BindableProperty<int>.Comparer = (a, b) => Math.Abs(a - b) < 1;
// 3. 使用实例方法设置比较器 // 3. 使用实例方法设置比较器
var position = new BindableProperty<Vector3>(Vector3.Zero) var position = new BindableProperty<Vector3>(Vector3.Zero)
.WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等 .WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等
// 4. 字符串比较器示例 // 4. 字符串比较器示例
var name = new BindableProperty<string>("Player") var name = new BindableProperty<string>("Player")
.WithComparer((a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); .WithComparer((a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase));
``` ```
### BindablePropertyUnRegister`<T>` ### BindablePropertyUnRegister`<T>`
可绑定属性的注销器,负责清理监听。 可绑定属性的注销器,负责清理监听。
**使用示例:** **使用示例:**
```csharp ```csharp
var unregister = health.Register(OnHealthChanged); var unregister = health.Register(OnHealthChanged);
// 当需要取消监听时 // 当需要取消监听时
unregister.UnRegister(); unregister.UnRegister();
``` ```
## BindableProperty 工作原理 ## BindableProperty 工作原理
BindableProperty 基于事件系统实现属性变化通知: BindableProperty 基于事件系统实现属性变化通知:
1. **值设置**:当设置 `Value` 属性时,首先进行值比较 1. **值设置**:当设置 `Value` 属性时,首先进行值比较
2. **变化检测**:使用 `EqualityComparer<T>.Default` 或自定义比较器检测值变化 2. **变化检测**:使用 `EqualityComparer<T>.Default` 或自定义比较器检测值变化
3. **事件触发**:如果值发生变化,调用所有注册的回调函数 3. **事件触发**:如果值发生变化,调用所有注册的回调函数
4. **内存管理**:通过 `IUnRegister` 机制管理监听器的生命周期 4. **内存管理**:通过 `IUnRegister` 机制管理监听器的生命周期
## 在 Model 中使用 ## 在 Model 中使用
### 定义可绑定属性 ### 定义可绑定属性
```csharp ```csharp
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// 可读写属性 // 可读写属性
public BindableProperty<string> Name { get; } = new("Player"); public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1); public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100); public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<Vector3> Position { get; } = new(Vector3.Zero); public BindableProperty<Vector3> Position { get; } = new(Vector3.Zero);
// 只读属性(外部只能读取和监听) // 只读属性(外部只能读取和监听)
public IReadonlyBindableProperty<int> ReadonlyHealth => Health; public IReadonlyBindableProperty<int> ReadonlyHealth => Health;
protected override void OnInit() protected override void OnInit()
{ {
// 内部监听属性变化 // 内部监听属性变化
Health.Register(hp => Health.Register(hp =>
{ {
if (hp <= 0) if (hp <= 0)
{ {
this.SendEvent(new PlayerDiedEvent()); this.SendEvent(new PlayerDiedEvent());
} }
else if (hp < MaxHealth.Value * 0.3f) else if (hp < MaxHealth.Value * 0.3f)
{ {
this.SendEvent(new LowHealthWarningEvent()); this.SendEvent(new LowHealthWarningEvent());
} }
}); });
// 监听等级变化 // 监听等级变化
Level.Register(newLevel => Level.Register(newLevel =>
{ {
this.SendEvent(new PlayerLevelUpEvent { NewLevel = newLevel }); this.SendEvent(new PlayerLevelUpEvent { NewLevel = newLevel });
}); });
} }
// 业务方法 // 业务方法
public void TakeDamage(int damage) public void TakeDamage(int damage)
{ {
Health.Value = Math.Max(0, Health.Value - damage); Health.Value = Math.Max(0, Health.Value - damage);
} }
public void Heal(int amount) public void Heal(int amount)
{ {
Health.Value = Math.Min(MaxHealth.Value, Health.Value + amount); Health.Value = Math.Min(MaxHealth.Value, Health.Value + amount);
} }
public float GetHealthPercentage() public float GetHealthPercentage()
{ {
return (float)Health.Value / MaxHealth.Value; return (float)Health.Value / MaxHealth.Value;
} }
} }
``` ```
## 在 Controller 中监听 ## 在 Controller 中监听
### UI 数据绑定 ### UI 数据绑定
```csharp ```csharp
public partial class PlayerUI : Control, IController using GFramework.Core.Abstractions.controller;
{ using GFramework.SourceGenerators.Abstractions.rule;
[Export] private Label _healthLabel;
[Export] private Label _nameLabel; [ContextAware]
[Export] private ProgressBar _healthBar; public partial class PlayerUI : Control, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList(); [Export] private Label _healthLabel;
[Export] private Label _nameLabel;
public IArchitecture GetArchitecture() => GameArchitecture.Interface; [Export] private ProgressBar _healthBar;
public override void _Ready() private IUnRegisterList _unregisterList = new UnRegisterList();
{
var playerModel = this.GetModel<PlayerModel>(); public override void _Ready()
{
// 绑定生命值到UI立即显示当前值 var playerModel = Context.GetModel<PlayerModel>();
playerModel.Health
.RegisterWithInitValue(health => // 绑定生命值到UI立即显示当前值
{ playerModel.Health
_healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}"; .RegisterWithInitValue(health =>
_healthBar.Value = (float)health / playerModel.MaxHealth.Value * 100; {
}) _healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}";
.AddToUnregisterList(_unregisterList); _healthBar.Value = (float)health / playerModel.MaxHealth.Value * 100;
})
// 绑定最大生命值 .AddToUnregisterList(_unregisterList);
playerModel.MaxHealth
.RegisterWithInitValue(maxHealth => // 绑定最大生命值
{ playerModel.MaxHealth
_healthBar.MaxValue = maxHealth; .RegisterWithInitValue(maxHealth =>
}) {
.AddToUnregisterList(_unregisterList); _healthBar.MaxValue = maxHealth;
})
// 绑定名称 .AddToUnregisterList(_unregisterList);
playerModel.Name
.RegisterWithInitValue(name => // 绑定名称
{ playerModel.Name
_nameLabel.Text = name; .RegisterWithInitValue(name =>
}) {
.AddToUnregisterList(_unregisterList); _nameLabel.Text = name;
})
// 绑定位置(仅用于调试显示) .AddToUnregisterList(_unregisterList);
playerModel.Position
.RegisterWithInitValue(pos => // 绑定位置(仅用于调试显示)
{ playerModel.Position
// 仅在调试模式下显示 .RegisterWithInitValue(pos =>
#if DEBUG {
Console.WriteLine($"Player position: {pos}"); // 仅在调试模式下显示
#endif #if DEBUG
}) Console.WriteLine($"Player position: {pos}");
.AddToUnregisterList(_unregisterList); #endif
} })
.AddToUnregisterList(_unregisterList);
public override void _ExitTree() }
{
_unregisterList.UnRegisterAll(); public override void _ExitTree()
} {
} _unregisterList.UnRegisterAll();
``` }
}
## 常见使用模式 ```
### 1. 双向绑定 ## 常见使用模式
```c# ### 1. 双向绑定
// Model
public class SettingsModel : AbstractModel ```c#
{ // Model
public BindableProperty<float> MasterVolume { get; } = new(1.0f); public class SettingsModel : AbstractModel
protected override void OnInit() { } {
} public BindableProperty<float> MasterVolume { get; } = new(1.0f);
protected override void OnInit() { }
// UI Controller }
public partial class VolumeSlider : HSlider, IController
{ // UI Controller
private BindableProperty<float> _volumeProperty; [ContextAware]
public partial class VolumeSlider : HSlider, IController
public override void _Ready() {
{ private BindableProperty<float> _volumeProperty;
_volumeProperty = this.GetModel<SettingsModel>().MasterVolume;
public override void _Ready()
// Model -> UI {
_volumeProperty.RegisterWithInitValue(vol => Value = vol) _volumeProperty = Context.GetModel<SettingsModel>().MasterVolume;
.UnRegisterWhenNodeExitTree(this);
// Model -> UI
// UI -> Model _volumeProperty.RegisterWithInitValue(vol => Value = vol)
ValueChanged += newValue => _volumeProperty.Value = (float)newValue; .UnRegisterWhenNodeExitTree(this);
}
} // UI -> Model
``` ValueChanged += newValue => _volumeProperty.Value = (float)newValue;
}
### 2. 计算属性 }
```
```c#
public class PlayerModel : AbstractModel ### 2. 计算属性
{
public BindableProperty<int> Health { get; } = new(100); ```c#
public BindableProperty<int> MaxHealth { get; } = new(100); public class PlayerModel : AbstractModel
public BindableProperty<float> HealthPercent { get; } = new(1.0f); {
public BindableProperty<int> Health { get; } = new(100);
protected override void OnInit() public BindableProperty<int> MaxHealth { get; } = new(100);
{ public BindableProperty<float> HealthPercent { get; } = new(1.0f);
// 自动计算百分比
Action updatePercent = () => protected override void OnInit()
{ {
HealthPercent.Value = (float)Health.Value / MaxHealth.Value; // 自动计算百分比
}; Action updatePercent = () =>
{
Health.Register(_ => updatePercent()); HealthPercent.Value = (float)Health.Value / MaxHealth.Value;
MaxHealth.Register(_ => updatePercent()); };
updatePercent(); // 初始计算 Health.Register(_ => updatePercent());
} MaxHealth.Register(_ => updatePercent());
}
``` updatePercent(); // 初始计算
}
### 3. 属性验证 }
```
```c#
public class PlayerModel : AbstractModel ### 3. 属性验证
{
private BindableProperty<int> _health = new(100); ```c#
public class PlayerModel : AbstractModel
public BindableProperty<int> Health {
{ private BindableProperty<int> _health = new(100);
get => _health;
set public BindableProperty<int> Health
{ {
// 限制范围 get => _health;
var clampedValue = Math.Clamp(value.Value, 0, MaxHealth.Value); set
_health.Value = clampedValue; {
} // 限制范围
} var clampedValue = Math.Clamp(value.Value, 0, MaxHealth.Value);
_health.Value = clampedValue;
public BindableProperty<int> MaxHealth { get; } = new(100); }
}
protected override void OnInit() { }
} public BindableProperty<int> MaxHealth { get; } = new(100);
```
protected override void OnInit() { }
### 4. 条件监听 }
```
```c#
public class CombatController : Node, IController ### 4. 条件监听
{
public override void _Ready() ```c#
{ using GFramework.Core.Abstractions.controller;
var playerModel = this.GetModel<PlayerModel>(); using GFramework.SourceGenerators.Abstractions.rule;
// 只在生命值低于30%时显示警告 [ContextAware]
playerModel.Health.Register(hp => public partial class CombatController : Node, IController
{ {
if (hp < playerModel.MaxHealth.Value * 0.3f) public override void _Ready()
{ {
ShowLowHealthWarning(); var playerModel = Context.GetModel<PlayerModel>();
}
else // 只在生命值低于30%时显示警告
{ playerModel.Health.Register(hp =>
HideLowHealthWarning(); {
} if (hp < playerModel.MaxHealth.Value * 0.3f)
}).UnRegisterWhenNodeExitTree(this); {
} ShowLowHealthWarning();
} }
``` else
{
## 性能优化 HideLowHealthWarning();
}
### 1. 避免频繁触发 }).UnRegisterWhenNodeExitTree(this);
}
```c# }
// 使用 SetValueWithoutEvent 批量修改 ```
public void LoadPlayerData(SaveData data)
{ ## 性能优化
// 临时关闭事件
Health.SetValueWithoutEvent(data.Health); ### 1. 避免频繁触发
Mana.SetValueWithoutEvent(data.Mana);
Gold.SetValueWithoutEvent(data.Gold); ```c#
// 使用 SetValueWithoutEvent 批量修改
// 最后统一触发一次更新事件 public void LoadPlayerData(SaveData data)
this.SendEvent(new PlayerDataLoadedEvent()); {
} // 临时关闭事件
``` Health.SetValueWithoutEvent(data.Health);
Mana.SetValueWithoutEvent(data.Mana);
### 2. 自定义比较器 Gold.SetValueWithoutEvent(data.Gold);
```c# // 最后统一触发一次更新事件
// 避免浮点数精度问题导致的频繁触发 this.SendEvent(new PlayerDataLoadedEvent());
var position = new BindableProperty<Vector3>() }
.WithComparer((a, b) => a.DistanceTo(b) < 0.001f); ```
```
### 2. 自定义比较器
## 实现原理
```c#
### 值变化检测 // 避免浮点数精度问题导致的频繁触发
var position = new BindableProperty<Vector3>()
```c# .WithComparer((a, b) => a.DistanceTo(b) < 0.001f);
// 使用 EqualityComparer<T>.Default 进行比较 ```
if (!EqualityComparer<T>.Default.Equals(value, MValue))
{ ## 实现原理
MValue = value;
_mOnValueChanged?.Invoke(value); ### 值变化检测
}
``` ```c#
// 使用 EqualityComparer<T>.Default 进行比较
### 事件触发机制 if (!EqualityComparer<T>.Default.Equals(value, MValue))
{
```c# MValue = value;
// 当值变化时触发所有注册的回调 _mOnValueChanged?.Invoke(value);
_mOnValueChanged?.Invoke(value); }
``` ```
## 最佳实践 ### 事件触发机制
1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层 ```c#
2. **使用只读接口暴露** - 防止外部随意修改 // 当值变化时触发所有注册的回调
3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree _mOnValueChanged?.Invoke(value);
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值 ```
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性 ## 最佳实践
## 相关包 1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层
2. **使用只读接口暴露** - 防止外部随意修改
- [`model`](./model.md) - Model 中大量使用 BindableProperty 3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree
- [`events`](./events.md) - BindableProperty 基于事件系统实现 4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
- [`extensions`](./extensions.md) - 提供便捷的注销扩展方法 5. **避免循环依赖** - 属性监听器中修改其他属性要小心
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
---
## 相关包
**许可证**: Apache 2.0
- [`model`](./model.md) - Model 中大量使用 BindableProperty
- [`events`](./events.md) - BindableProperty 基于事件系统实现
- [`extensions`](./extensions.md) - 提供便捷的注销扩展方法
---
**许可证**: Apache 2.0

View File

@ -144,13 +144,15 @@ public class LoadPlayerDataQuery : AbstractAsyncQuery<LoadPlayerDataInput, Playe
### 2. 发送查询 ### 2. 发送查询
```csharp ```csharp
public class ShopUI : IController using GFramework.Core.Abstractions.controller;
using GFramework.SourceGenerators.Abstractions.rule;
[ContextAware]
public partial class ShopUI : IController
{ {
[Export] private Button _buyButton; [Export] private Button _buyButton;
[Export] private int _itemPrice = 100; [Export] private int _itemPrice = 100;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void OnReady() public void OnReady()
{ {
_buyButton.Pressed += OnBuyButtonPressed; _buyButton.Pressed += OnBuyButtonPressed;
@ -160,12 +162,12 @@ public class ShopUI : IController
{ {
// 查询玩家金币 // 查询玩家金币
var query = new GetPlayerGoldQuery { Input = new GetPlayerGoldInput() }; var query = new GetPlayerGoldQuery { Input = new GetPlayerGoldInput() };
int playerGold = this.SendQuery(query); int playerGold = Context.SendQuery(query);
if (playerGold >= _itemPrice) if (playerGold >= _itemPrice)
{ {
// 发送购买命令 // 发送购买命令
this.SendCommand(new BuyItemCommand { Input = new BuyItemInput { ItemId = "sword_01" } }); Context.SendCommand(new BuyItemCommand { Input = new BuyItemInput { ItemId = "sword_01" } });
} }
else else
{ {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,320 +1,323 @@
# 快速开始 # 快速开始
本指南将帮助您快速构建第一个基于 GFramework 的应用程序。 本指南将帮助您快速构建第一个基于 GFramework 的应用程序。
## 1. 创建项目架构 ## 1. 创建项目架构
首先定义您的应用架构: 首先定义您的应用架构:
```csharp ```csharp
using GFramework.Core.architecture; using GFramework.Core.architecture;
public class GameArchitecture : Architecture public class GameArchitecture : Architecture
{ {
protected override void Init() protected override void Init()
{ {
// 注册模型 - 存储应用状态 // 注册模型 - 存储应用状态
RegisterModel(new PlayerModel()); RegisterModel(new PlayerModel());
RegisterModel(new GameStateModel()); RegisterModel(new GameStateModel());
// 注册系统 - 处理业务逻辑 // 注册系统 - 处理业务逻辑
RegisterSystem(new PlayerSystem()); RegisterSystem(new PlayerSystem());
RegisterSystem(new GameLogicSystem()); RegisterSystem(new GameLogicSystem());
// 注册工具类 - 提供辅助功能 // 注册工具类 - 提供辅助功能
RegisterUtility(new StorageUtility()); RegisterUtility(new StorageUtility());
} }
} }
``` ```
## 2. 定义数据模型 ## 2. 定义数据模型
创建您的数据模型: 创建您的数据模型:
```csharp ```csharp
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// 使用可绑定属性实现响应式数据 // 使用可绑定属性实现响应式数据
public BindableProperty<string> Name { get; } = new("Player"); public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Score { get; } = new(0); public BindableProperty<int> Score { get; } = new(0);
protected override void OnInit() protected override void OnInit()
{ {
// 监听健康值变化 // 监听健康值变化
Health.Register(OnHealthChanged); Health.Register(OnHealthChanged);
} }
private void OnHealthChanged(int newHealth) private void OnHealthChanged(int newHealth)
{ {
if (newHealth <= 0) if (newHealth <= 0)
{ {
this.SendEvent(new PlayerDiedEvent()); this.SendEvent(new PlayerDiedEvent());
} }
} }
} }
public class GameStateModel : AbstractModel public class GameStateModel : AbstractModel
{ {
public BindableProperty<bool> IsGameRunning { get; } = new(false); public BindableProperty<bool> IsGameRunning { get; } = new(false);
public BindableProperty<int> CurrentLevel { get; } = new(1); public BindableProperty<int> CurrentLevel { get; } = new(1);
} }
``` ```
## 3. 实现业务逻辑 ## 3. 实现业务逻辑
创建处理业务逻辑的系统: 创建处理业务逻辑的系统:
```csharp ```csharp
public class PlayerSystem : AbstractSystem public class PlayerSystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
// 监听玩家输入事件 // 监听玩家输入事件
this.RegisterEvent<PlayerMoveEvent>(OnPlayerMove); this.RegisterEvent<PlayerMoveEvent>(OnPlayerMove);
this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack); this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack);
} }
private void OnPlayerMove(PlayerMoveEvent e) private void OnPlayerMove(PlayerMoveEvent e)
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
// 处理移动逻辑 // 处理移动逻辑
Console.WriteLine($"Player moved to {e.Direction}"); Console.WriteLine($"Player moved to {e.Direction}");
} }
private void OnPlayerAttack(PlayerAttackEvent e) private void OnPlayerAttack(PlayerAttackEvent e)
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
// 处理攻击逻辑 // 处理攻击逻辑
playerModel.Score.Value += 10; playerModel.Score.Value += 10;
this.SendEvent(new EnemyDamagedEvent { Damage = 25 }); this.SendEvent(new EnemyDamagedEvent { Damage = 25 });
} }
} }
public class GameLogicSystem : AbstractSystem public class GameLogicSystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<EnemyDamagedEvent>(OnEnemyDamaged); this.RegisterEvent<EnemyDamagedEvent>(OnEnemyDamaged);
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied); this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
} }
private void OnEnemyDamaged(EnemyDamagedEvent e) private void OnEnemyDamaged(EnemyDamagedEvent e)
{ {
Console.WriteLine($"Enemy took {e.Damage} damage"); Console.WriteLine($"Enemy took {e.Damage} damage");
// 检查是否需要升级关卡 // 检查是否需要升级关卡
CheckLevelProgress(); CheckLevelProgress();
} }
private void OnPlayerDied(PlayerDiedEvent e) private void OnPlayerDied(PlayerDiedEvent e)
{ {
var gameState = this.GetModel<GameStateModel>(); var gameState = this.GetModel<GameStateModel>();
gameState.IsGameRunning.Value = false; gameState.IsGameRunning.Value = false;
Console.WriteLine("Game Over!"); Console.WriteLine("Game Over!");
} }
private void CheckLevelProgress() private void CheckLevelProgress()
{ {
// 实现关卡进度检查逻辑 // 实现关卡进度检查逻辑
} }
} }
``` ```
## 4. 定义事件 ## 4. 定义事件
创建应用中使用的事件: 创建应用中使用的事件:
```csharp ```csharp
public class PlayerMoveEvent : IEvent public class PlayerMoveEvent : IEvent
{ {
public Vector2 Direction { get; set; } public Vector2 Direction { get; set; }
} }
public class PlayerAttackEvent : IEvent public class PlayerAttackEvent : IEvent
{ {
public Vector2 TargetPosition { get; set; } public Vector2 TargetPosition { get; set; }
} }
public class PlayerDiedEvent : IEvent public class PlayerDiedEvent : IEvent
{ {
// 玩家死亡事件 // 玩家死亡事件
} }
public class EnemyDamagedEvent : IEvent public class EnemyDamagedEvent : IEvent
{ {
public int Damage { get; set; } public int Damage { get; set; }
} }
``` ```
## 5. 创建控制器 ## 5. 创建控制器
实现控制器来连接 UI 和业务逻辑: 实现控制器来连接 UI 和业务逻辑:
```csharp ```csharp
public class GameController : IController using GFramework.Core.Abstractions.controller;
{ using GFramework.SourceGenerators.Abstractions.rule;
private IArchitecture _architecture;
private PlayerModel _playerModel; [ContextAware]
private GameStateModel _gameStateModel; public partial class GameController : IController
{
public GameController(IArchitecture architecture) private PlayerModel _playerModel;
{ private GameStateModel _gameStateModel;
_architecture = architecture;
_playerModel = architecture.GetModel<PlayerModel>(); public void Initialize()
_gameStateModel = architecture.GetModel<GameStateModel>(); {
_playerModel = Context.GetModel<PlayerModel>();
// 初始化事件监听 _gameStateModel = Context.GetModel<GameStateModel>();
InitializeEventListeners();
} // 初始化事件监听
InitializeEventListeners();
private void InitializeEventListeners() }
{
// 监听模型变化并更新 UI private void InitializeEventListeners()
_playerModel.Health.RegisterWithInitValue(OnHealthChanged); {
_playerModel.Score.RegisterWithInitValue(OnScoreChanged); // 监听模型变化并更新 UI
_gameStateModel.IsGameRunning.Register(OnGameStateChanged); _playerModel.Health.RegisterWithInitValue(OnHealthChanged);
} _playerModel.Score.RegisterWithInitValue(OnScoreChanged);
_gameStateModel.IsGameRunning.Register(OnGameStateChanged);
public void StartGame() }
{
_gameStateModel.IsGameRunning.Value = true; public void StartGame()
_architecture.SendEvent(new GameStartEvent()); {
Console.WriteLine("Game started!"); _gameStateModel.IsGameRunning.Value = true;
} Context.SendEvent(new GameStartEvent());
Console.WriteLine("Game started!");
public void MovePlayer(Vector2 direction) }
{
_architecture.SendCommand(new MovePlayerCommand { Direction = direction }); public void MovePlayer(Vector2 direction)
} {
Context.SendCommand(new MovePlayerCommand { Direction = direction });
public void PlayerAttack(Vector2 target) }
{
_architecture.SendCommand(new AttackCommand { TargetPosition = target }); public void PlayerAttack(Vector2 target)
} {
Context.SendCommand(new AttackCommand { TargetPosition = target });
// UI 更新回调 }
private void OnHealthChanged(int health)
{ // UI 更新回调
UpdateHealthDisplay(health); private void OnHealthChanged(int health)
} {
UpdateHealthDisplay(health);
private void OnScoreChanged(int score) }
{
UpdateScoreDisplay(score); private void OnScoreChanged(int score)
} {
UpdateScoreDisplay(score);
private void OnGameStateChanged(bool isRunning) }
{
UpdateGameStatusDisplay(isRunning); private void OnGameStateChanged(bool isRunning)
} {
UpdateGameStatusDisplay(isRunning);
private void UpdateHealthDisplay(int health) }
{
// 更新血条 UI private void UpdateHealthDisplay(int health)
Console.WriteLine($"Health: {health}"); {
} // 更新血条 UI
Console.WriteLine($"Health: {health}");
private void UpdateScoreDisplay(int score) }
{
// 更新分数显示 private void UpdateScoreDisplay(int score)
Console.WriteLine($"Score: {score}"); {
} // 更新分数显示
Console.WriteLine($"Score: {score}");
private void UpdateGameStatusDisplay(bool isRunning) }
{
// 更新游戏状态显示 private void UpdateGameStatusDisplay(bool isRunning)
Console.WriteLine($"Game running: {isRunning}"); {
} // 更新游戏状态显示
} Console.WriteLine($"Game running: {isRunning}");
``` }
}
## 6. 定义命令 ```
创建命令来封装用户操作: ## 6. 定义命令
```csharp 创建命令来封装用户操作:
public class MovePlayerCommand : AbstractCommand
{ ```csharp
public Vector2 Direction { get; set; } public class MovePlayerCommand : AbstractCommand
{
protected override void OnDo() public Vector2 Direction { get; set; }
{
// 发送移动事件 protected override void OnDo()
this.SendEvent(new PlayerMoveEvent { Direction = Direction }); {
} // 发送移动事件
} this.SendEvent(new PlayerMoveEvent { Direction = Direction });
}
public class AttackCommand : AbstractCommand }
{
public Vector2 TargetPosition { get; set; } public class AttackCommand : AbstractCommand
{
protected override void OnDo() public Vector2 TargetPosition { get; set; }
{
// 发送攻击事件 protected override void OnDo()
this.SendEvent(new PlayerAttackEvent { TargetPosition = TargetPosition }); {
} // 发送攻击事件
} this.SendEvent(new PlayerAttackEvent { TargetPosition = TargetPosition });
``` }
}
## 7. 运行应用 ```
现在让我们运行这个简单的应用: ## 7. 运行应用
```csharp 现在让我们运行这个简单的应用:
class Program
{ ```csharp
static void Main(string[] args) class Program
{ {
// 创建并初始化架构 static void Main(string[] args)
var architecture = new GameArchitecture(); {
architecture.Initialize(); // 创建并初始化架构
var architecture = new GameArchitecture();
// 创建控制器 architecture.Initialize();
var gameController = new GameController(architecture);
// 创建控制器
// 开始游戏 var gameController = new GameController();
gameController.StartGame(); gameController.Initialize();
// 模拟玩家操作 // 开始游戏
gameController.MovePlayer(new Vector2(1, 0)); gameController.StartGame();
gameController.PlayerAttack(new Vector2(5, 5));
// 模拟玩家操作
// 模拟玩家受伤 gameController.MovePlayer(new Vector2(1, 0));
var playerModel = architecture.GetModel<PlayerModel>(); gameController.PlayerAttack(new Vector2(5, 5));
playerModel.Health.Value = 50;
// 模拟玩家受伤
// 模拟玩家死亡 var playerModel = architecture.GetModel<PlayerModel>();
playerModel.Health.Value = 0; playerModel.Health.Value = 50;
Console.WriteLine("Press any key to exit..."); // 模拟玩家死亡
Console.ReadKey(); playerModel.Health.Value = 0;
}
} Console.WriteLine("Press any key to exit...");
``` Console.ReadKey();
}
## 8. 运行结果 }
```
执行程序后,您应该看到类似以下输出:
## 8. 运行结果
```
Game started! 执行程序后,您应该看到类似以下输出:
Game running: True
Player moved to (1, 0) ```
Player took 25 damage Game started!
Score: 10 Game running: True
Health: 50 Player moved to (1, 0)
Health: 0 Player took 25 damage
Player died Score: 10
Game Over! Health: 50
Game running: False Health: 0
Press any key to exit... Player died
``` Game Over!
Game running: False
## 下一步 Press any key to exit...
```
这个简单的示例展示了 GFramework 的核心概念:
## 下一步
1. **架构模式** - 清晰的分层结构
2. **响应式数据** - BindableProperty 自动更新 这个简单的示例展示了 GFramework 的核心概念:
3. **事件驱动** - 松耦合的组件通信
4. **命令模式** - 封装用户操作 1. **架构模式** - 清晰的分层结构
2. **响应式数据** - BindableProperty 自动更新
3. **事件驱动** - 松耦合的组件通信
4. **命令模式** - 封装用户操作

File diff suppressed because it is too large Load Diff

View File

@ -1,377 +1,403 @@
# ContextAware 生成器 # ContextAware 生成器
> 自动实现 IContextAware 接口,提供架构上下文访问能力 > 自动实现 IContextAware 接口,提供架构上下文访问能力
## 概述 ## 概述
ContextAware 生成器为标记了 `[ContextAware]` 属性的类自动生成 `IContextAware` 接口实现,使类能够便捷地访问架构上下文( ContextAware 生成器为标记了 `[ContextAware]` 属性的类自动生成 `IContextAware` 接口实现,使类能够便捷地访问架构上下文(
`IArchitectureContext`)。这是 GFramework 中最常用的源码生成器之一,几乎所有需要与架构交互的组件都会使用它。 `IArchitectureContext`)。这是 GFramework 中最常用的源码生成器之一,几乎所有需要与架构交互的组件都会使用它。
### 核心功能 ### 核心功能
- **自动接口实现**:无需手动实现 `IContextAware` 接口的 `SetContext()``GetContext()` 方法 - **自动接口实现**:无需手动实现 `IContextAware` 接口的 `SetContext()``GetContext()` 方法
- **懒加载上下文**`Context` 属性在首次访问时自动初始化 - **懒加载上下文**`Context` 属性在首次访问时自动初始化
- **默认提供者**:使用 `GameContextProvider` 作为默认上下文提供者 - **默认提供者**:使用 `GameContextProvider` 作为默认上下文提供者
- **测试友好**:支持通过 `SetContextProvider()` 配置自定义上下文提供者 - **测试友好**:支持通过 `SetContextProvider()` 配置自定义上下文提供者
## 基础使用 ## 基础使用
### 标记类 ### 标记类
使用 `[ContextAware]` 属性标记需要访问架构上下文的类: 使用 `[ContextAware]` 属性标记需要访问架构上下文的类:
```csharp ```csharp
using GFramework.SourceGenerators.Abstractions.rule; using GFramework.SourceGenerators.Abstractions.rule;
using GFramework.Core.Abstractions.controller; using GFramework.Core.Abstractions.controller;
[ContextAware] [ContextAware]
public partial class PlayerController : IController public partial class PlayerController : IController
{ {
public void Initialize() public void Initialize()
{ {
// Context 属性自动生成,提供架构上下文访问 // Context 属性自动生成,提供架构上下文访问
var playerModel = Context.GetModel<PlayerModel>(); var playerModel = Context.GetModel<PlayerModel>();
var combatSystem = Context.GetSystem<CombatSystem>(); var combatSystem = Context.GetSystem<CombatSystem>();
Context.SendEvent(new PlayerInitializedEvent()); Context.SendEvent(new PlayerInitializedEvent());
} }
public void Attack(Enemy target) public void Attack(Enemy target)
{ {
var damage = Context.GetUtility<DamageCalculator>().Calculate(this, target); var damage = Context.GetUtility<DamageCalculator>().Calculate(this, target);
Context.SendCommand(new DealDamageCommand(target, damage)); Context.SendCommand(new DealDamageCommand(target, damage));
} }
} }
``` ```
### 必要条件 ### 必要条件
标记的类必须满足以下条件: 标记的类必须满足以下条件:
1. **必须是 `partial` 类**:生成器需要生成部分类代码 1. **必须是 `partial` 类**:生成器需要生成部分类代码
2. **必须是 `class` 类型**:不能是 `struct``interface` 2. **必须是 `class` 类型**:不能是 `struct``interface`
```csharp ```csharp
// ✅ 正确 // ✅ 正确
[ContextAware] [ContextAware]
public partial class MyController { } public partial class MyController { }
// ❌ 错误:缺少 partial 关键字 // ❌ 错误:缺少 partial 关键字
[ContextAware] [ContextAware]
public class MyController { } public class MyController { }
// ❌ 错误:不能用于 struct // ❌ 错误:不能用于 struct
[ContextAware] [ContextAware]
public partial struct MyStruct { } public partial struct MyStruct { }
``` ```
## 生成的代码 ## 生成的代码
编译器会为标记的类自动生成以下代码: 编译器会为标记的类自动生成以下代码:
```csharp ```csharp
// <auto-generated/> // <auto-generated/>
#nullable enable #nullable enable
namespace YourNamespace; namespace YourNamespace;
partial class PlayerController : global::GFramework.Core.Abstractions.rule.IContextAware partial class PlayerController : global::GFramework.Core.Abstractions.rule.IContextAware
{ {
private global::GFramework.Core.Abstractions.architecture.IArchitectureContext? _context; private global::GFramework.Core.Abstractions.architecture.IArchitectureContext? _context;
private static global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider? _contextProvider; private static global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider? _contextProvider;
/// <summary> /// <summary>
/// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider /// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider
/// </summary> /// </summary>
protected global::GFramework.Core.Abstractions.architecture.IArchitectureContext Context protected global::GFramework.Core.Abstractions.architecture.IArchitectureContext Context
{ {
get get
{ {
if (_context == null) if (_context == null)
{ {
_contextProvider ??= new global::GFramework.Core.architecture.GameContextProvider(); _contextProvider ??= new global::GFramework.Core.architecture.GameContextProvider();
_context = _contextProvider.GetContext(); _context = _contextProvider.GetContext();
} }
return _context; return _context;
} }
} }
/// <summary> /// <summary>
/// 配置上下文提供者(用于测试或多架构场景) /// 配置上下文提供者(用于测试或多架构场景)
/// </summary> /// </summary>
/// <param name="provider">上下文提供者实例</param> /// <param name="provider">上下文提供者实例</param>
public static void SetContextProvider(global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider provider) public static void SetContextProvider(global::GFramework.Core.Abstractions.architecture.IArchitectureContextProvider provider)
{ {
_contextProvider = provider; _contextProvider = provider;
} }
/// <summary> /// <summary>
/// 重置上下文提供者为默认值(用于测试清理) /// 重置上下文提供者为默认值(用于测试清理)
/// </summary> /// </summary>
public static void ResetContextProvider() public static void ResetContextProvider()
{ {
_contextProvider = null; _contextProvider = null;
} }
void global::GFramework.Core.Abstractions.rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.architecture.IArchitectureContext context) void global::GFramework.Core.Abstractions.rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.architecture.IArchitectureContext context)
{ {
_context = context; _context = context;
} }
global::GFramework.Core.Abstractions.architecture.IArchitectureContext global::GFramework.Core.Abstractions.rule.IContextAware.GetContext() global::GFramework.Core.Abstractions.architecture.IArchitectureContext global::GFramework.Core.Abstractions.rule.IContextAware.GetContext()
{ {
return Context; return Context;
} }
} }
``` ```
### 代码解析 ### 代码解析
生成的代码包含以下关键部分: 生成的代码包含以下关键部分:
1. **私有字段** 1. **私有字段**
- `_context`:缓存的上下文实例 - `_context`:缓存的上下文实例
- `_contextProvider`:静态上下文提供者(所有实例共享) - `_contextProvider`:静态上下文提供者(所有实例共享)
2. **Context 属性** 2. **Context 属性**
- `protected` 访问级别,子类可访问 - `protected` 访问级别,子类可访问
- 懒加载:首次访问时自动初始化 - 懒加载:首次访问时自动初始化
- 使用 `GameContextProvider` 作为默认提供者 - 使用 `GameContextProvider` 作为默认提供者
3. **配置方法** 3. **配置方法**
- `SetContextProvider()`:设置自定义上下文提供者 - `SetContextProvider()`:设置自定义上下文提供者
- `ResetContextProvider()`:重置为默认提供者 - `ResetContextProvider()`:重置为默认提供者
4. **显式接口实现** 4. **显式接口实现**
- `IContextAware.SetContext()`:允许外部设置上下文 - `IContextAware.SetContext()`:允许外部设置上下文
- `IContextAware.GetContext()`:返回当前上下文 - `IContextAware.GetContext()`:返回当前上下文
## 配置上下文提供者 ## 配置上下文提供者
### 测试场景 ### 测试场景
在单元测试中,通常需要使用自定义的上下文提供者: 在单元测试中,通常需要使用自定义的上下文提供者:
```csharp ```csharp
[Test] [Test]
public async Task TestPlayerController() public async Task TestPlayerController()
{ {
// 创建测试架构 // 创建测试架构
var testArchitecture = new TestArchitecture(); var testArchitecture = new TestArchitecture();
await testArchitecture.InitAsync(); await testArchitecture.InitAsync();
// 配置自定义上下文提供者 // 配置自定义上下文提供者
PlayerController.SetContextProvider(new TestContextProvider(testArchitecture)); PlayerController.SetContextProvider(new TestContextProvider(testArchitecture));
try try
{ {
// 测试代码 // 测试代码
var controller = new PlayerController(); var controller = new PlayerController();
controller.Initialize(); controller.Initialize();
// 验证... // 验证...
} }
finally finally
{ {
// 清理:重置上下文提供者 // 清理:重置上下文提供者
PlayerController.ResetContextProvider(); PlayerController.ResetContextProvider();
} }
} }
``` ```
### 多架构场景 ### 多架构场景
在某些高级场景中,可能需要同时运行多个架构实例: 在某些高级场景中,可能需要同时运行多个架构实例:
```csharp ```csharp
public class MultiArchitectureManager public class MultiArchitectureManager
{ {
private readonly Dictionary<string, IArchitecture> _architectures = new(); private readonly Dictionary<string, IArchitecture> _architectures = new();
public void SwitchToArchitecture(string name) public void SwitchToArchitecture(string name)
{ {
var architecture = _architectures[name]; var architecture = _architectures[name];
var provider = new ScopedContextProvider(architecture); var provider = new ScopedContextProvider(architecture);
// 为所有使用 [ContextAware] 的类切换上下文 // 为所有使用 [ContextAware] 的类切换上下文
PlayerController.SetContextProvider(provider); PlayerController.SetContextProvider(provider);
EnemyController.SetContextProvider(provider); EnemyController.SetContextProvider(provider);
// ... // ...
} }
} }
``` ```
## 使用场景 ## 使用场景
### 何时使用 [ContextAware] ### 何时使用 [ContextAware]
推荐在以下场景使用 `[ContextAware]` 属性: 推荐在以下场景使用 `[ContextAware]` 属性:
1. **Controller 层**:需要协调多个 Model/System 的控制器 1. **Controller 层**:需要协调多个 Model/System 的控制器
2. **Command/Query 实现**:需要访问架构服务的命令或查询 2. **Command/Query 实现**:需要访问架构服务的命令或查询
3. **自定义组件**:不继承框架基类但需要上下文访问的组件 3. **自定义组件**:不继承框架基类但需要上下文访问的组件
```csharp ```csharp
[ContextAware] [ContextAware]
public partial class GameFlowController : IController public partial class GameFlowController : IController
{ {
public async Task StartGame() public async Task StartGame()
{ {
var saveSystem = Context.GetSystem<SaveSystem>(); var saveSystem = Context.GetSystem<SaveSystem>();
var uiSystem = Context.GetSystem<UISystem>(); var uiSystem = Context.GetSystem<UISystem>();
await saveSystem.LoadAsync(); await saveSystem.LoadAsync();
await uiSystem.ShowMainMenuAsync(); await uiSystem.ShowMainMenuAsync();
} }
} }
``` ```
### 何时继承 ContextAwareBase ### 与 IController 配合使用
如果类需要更多框架功能(如生命周期管理),应继承 `ContextAwareBase` 在 Godot 项目中,控制器通常同时实现 `IController` 和使用 `[ContextAware]`
```csharp ```csharp
// 推荐:需要生命周期管理时继承基类 using GFramework.Core.Abstractions.controller;
public class PlayerModel : AbstractModel using GFramework.SourceGenerators.Abstractions.rule;
{
// AbstractModel 已经继承了 ContextAwareBase [ContextAware]
protected override void OnInit() public partial class PlayerController : Node, IController
{ {
var config = Context.GetUtility<ConfigLoader>().Load<PlayerConfig>(); public override void _Ready()
} {
} // Context 属性由 [ContextAware] 自动生成
var playerModel = Context.GetModel<PlayerModel>();
// 推荐:简单组件使用属性 var combatSystem = Context.GetSystem<CombatSystem>();
[ContextAware] }
public partial class SimpleHelper }
{ ```
public void DoSomething()
{ **说明**
Context.SendEvent(new SomethingHappenedEvent());
} - `IController` 是标记接口,标识这是一个控制器
} - `[ContextAware]` 提供架构访问能力
``` - 两者配合使用是推荐的模式
## 与 IContextAware 接口的关系 ### 何时继承 ContextAwareBase
生成的代码实现了 `IContextAware` 接口: 如果类需要更多框架功能(如生命周期管理),应继承 `ContextAwareBase`
```csharp ```csharp
namespace GFramework.Core.Abstractions.rule; // 推荐:需要生命周期管理时继承基类
public class PlayerModel : AbstractModel
public interface IContextAware {
{ // AbstractModel 已经继承了 ContextAwareBase
void SetContext(IArchitectureContext context); protected override void OnInit()
IArchitectureContext GetContext(); {
} var config = Context.GetUtility<ConfigLoader>().Load<PlayerConfig>();
``` }
}
这意味着标记了 `[ContextAware]` 的类可以:
// 推荐:简单组件使用属性
1. **被架构自动注入上下文**:实现 `IContextAware` 的类在注册到架构时会自动调用 `SetContext()` [ContextAware]
2. **参与依赖注入**:可以作为 `IContextAware` 类型注入到其他组件 public partial class SimpleHelper
3. **支持上下文传递**:可以通过 `GetContext()` 将上下文传递给其他组件 {
public void DoSomething()
## 最佳实践 {
Context.SendEvent(new SomethingHappenedEvent());
### 1. 始终使用 partial 关键字 }
}
```csharp ```
// ✅ 正确
[ContextAware] ## 与 IContextAware 接口的关系
public partial class MyController { }
生成的代码实现了 `IContextAware` 接口:
// ❌ 错误:编译器会报错
[ContextAware] ```csharp
public class MyController { } namespace GFramework.Core.Abstractions.rule;
```
public interface IContextAware
### 2. 在测试中清理上下文提供者 {
void SetContext(IArchitectureContext context);
```csharp IArchitectureContext GetContext();
[TearDown] }
public void TearDown() ```
{
// 避免测试之间的状态污染 这意味着标记了 `[ContextAware]` 的类可以:
PlayerController.ResetContextProvider();
EnemyController.ResetContextProvider(); 1. **被架构自动注入上下文**:实现 `IContextAware` 的类在注册到架构时会自动调用 `SetContext()`
} 2. **参与依赖注入**:可以作为 `IContextAware` 类型注入到其他组件
``` 3. **支持上下文传递**:可以通过 `GetContext()` 将上下文传递给其他组件
### 3. 避免在构造函数中访问 Context ## 最佳实践
```csharp ### 1. 始终使用 partial 关键字
[ContextAware]
public partial class MyController ```csharp
{ // ✅ 正确
// ❌ 错误:构造函数执行时上下文可能未初始化 [ContextAware]
public MyController() public partial class MyController { }
{
var model = Context.GetModel<SomeModel>(); // 可能为 null // ❌ 错误:编译器会报错
} [ContextAware]
public class MyController { }
// ✅ 正确:在初始化方法中访问 ```
public void Initialize()
{ ### 2. 在测试中清理上下文提供者
var model = Context.GetModel<SomeModel>(); // 安全
} ```csharp
} [TearDown]
``` public void TearDown()
{
### 4. 优先使用 Context 属性而非接口方法 // 避免测试之间的状态污染
PlayerController.ResetContextProvider();
```csharp EnemyController.ResetContextProvider();
[ContextAware] }
public partial class MyController ```
{
public void DoSomething() ### 3. 避免在构造函数中访问 Context
{
// ✅ 推荐:使用生成的 Context 属性 ```csharp
var model = Context.GetModel<SomeModel>(); [ContextAware]
public partial class MyController
// ❌ 不推荐:显式调用接口方法 {
var context = ((IContextAware)this).GetContext(); // ❌ 错误:构造函数执行时上下文可能未初始化
var model2 = context.GetModel<SomeModel>(); public MyController()
} {
} var model = Context.GetModel<SomeModel>(); // 可能为 null
``` }
## 诊断信息 // ✅ 正确:在初始化方法中访问
public void Initialize()
生成器会在以下情况报告编译错误: {
var model = Context.GetModel<SomeModel>(); // 安全
### GFSG001: 类必须是 partial }
}
```csharp ```
[ContextAware]
public class MyController { } // 错误:缺少 partial 关键字 ### 4. 优先使用 Context 属性而非接口方法
```
```csharp
**解决方案**:添加 `partial` 关键字 [ContextAware]
public partial class MyController
```csharp {
[ContextAware] public void DoSomething()
public partial class MyController { } // ✅ 正确 {
``` // ✅ 推荐:使用生成的 Context 属性
var model = Context.GetModel<SomeModel>();
### GFSG002: ContextAware 只能用于类
// ❌ 不推荐:显式调用接口方法
```csharp var context = ((IContextAware)this).GetContext();
[ContextAware] var model2 = context.GetModel<SomeModel>();
public partial struct MyStruct { } // 错误:不能用于 struct }
``` }
```
**解决方案**:将 `struct` 改为 `class`
## 诊断信息
```csharp
[ContextAware] 生成器会在以下情况报告编译错误:
public partial class MyClass { } // ✅ 正确
``` ### GFSG001: 类必须是 partial
## 相关文档 ```csharp
[ContextAware]
- [Source Generators 概述](./index) public class MyController { } // 错误:缺少 partial 关键字
- [架构上下文](../core/context) ```
- [IContextAware 接口](../core/rule)
- [日志生成器](./logging-generator) **解决方案**:添加 `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)

File diff suppressed because it is too large Load Diff

View File

@ -1,337 +1,341 @@
# 日志生成器 # 日志生成器
> GFramework.SourceGenerators 自动生成日志代码,减少样板代码 > GFramework.SourceGenerators 自动生成日志代码,减少样板代码
## 概述 ## 概述
日志生成器是一个 Source Generator它会自动为标记了 `[Log]` 特性的类生成 ILogger 字段。这消除了手动编写日志字段的需要,让开发者专注于业务逻辑。 日志生成器是一个 Source Generator它会自动为标记了 `[Log]` 特性的类生成 ILogger 字段。这消除了手动编写日志字段的需要,让开发者专注于业务逻辑。
## 基本用法 ## 基本用法
### 标记类 ### 标记类
```csharp ```csharp
using GFramework.SourceGenerators.Abstractions.logging; using GFramework.SourceGenerators.Abstractions.logging;
[Log] [Log]
public partial class MyService public partial class MyService
{ {
public void DoSomething() public void DoSomething()
{ {
// 自动生成的 Logger 字段可直接使用 // 自动生成的 Logger 字段可直接使用
Logger.Info("执行操作"); Logger.Info("执行操作");
} }
} }
``` ```
### 生成代码 ### 生成代码
上面的代码会被编译时转换为: 上面的代码会被编译时转换为:
```csharp ```csharp
// <auto-generated/> // <auto-generated/>
public partial class MyService public partial class MyService
{ {
private static readonly ILogger Logger = private static readonly ILogger Logger =
LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.MyService"); LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.MyService");
} }
``` ```
**注意**:生成器只生成 ILogger 字段不生成日志方法。日志方法Info、Debug、Error 等)来自 ILogger 接口本身。 **注意**:生成器只生成 ILogger 字段不生成日志方法。日志方法Info、Debug、Error 等)来自 ILogger 接口本身。
## 日志级别 ## 日志级别
生成的 Logger 字段支持 ILogger 接口的所有方法: 生成的 Logger 字段支持 ILogger 接口的所有方法:
```csharp ```csharp
[Log] [Log]
public partial class MyClass public partial class MyClass
{ {
public void Example() public void Example()
{ {
// 调试信息 // 调试信息
Logger.Debug($"调试信息: {value}"); Logger.Debug($"调试信息: {value}");
// 普通信息 // 普通信息
Logger.Info("操作成功"); Logger.Info("操作成功");
// 警告 // 警告
Logger.Warning($"警告: {message}"); Logger.Warning($"警告: {message}");
// 错误 // 错误
Logger.Error($"错误: {ex.Message}"); Logger.Error($"错误: {ex.Message}");
// 严重错误 // 严重错误
Logger.Critical("系统故障"); Logger.Critical("系统故障");
} }
} }
``` ```
## 自定义日志类别 ## 自定义日志类别
```csharp ```csharp
[Log("Gameplay")] [Log("Gameplay")]
public partial class GameplaySystem public partial class GameplaySystem
{ {
// 日志会标记为 Gameplay 类别 // 日志会标记为 Gameplay 类别
public void Update() public void Update()
{ {
Logger.Info("游戏逻辑更新"); Logger.Info("游戏逻辑更新");
} }
} }
``` ```
## 配置选项 ## 配置选项
### 自定义字段名称 ### 自定义字段名称
```csharp ```csharp
[Log(FieldName = "_customLogger")] [Log(FieldName = "_customLogger")]
public partial class MyClass public partial class MyClass
{ {
public void DoSomething() public void DoSomething()
{ {
// 使用自定义字段名 // 使用自定义字段名
_customLogger.Info("使用自定义日志器"); _customLogger.Info("使用自定义日志器");
} }
} }
``` ```
### 非静态字段 ### 非静态字段
```csharp ```csharp
[Log(IsStatic = false)] [Log(IsStatic = false)]
public partial class InstanceLogger public partial class InstanceLogger
{ {
// 生成实例字段而非静态字段 // 生成实例字段而非静态字段
public void LogMessage() public void LogMessage()
{ {
Logger.Info("实例日志"); Logger.Info("实例日志");
} }
} }
``` ```
### 访问修饰符 ### 访问修饰符
```csharp ```csharp
[Log(AccessModifier = "protected")] [Log(AccessModifier = "protected")]
public partial class ProtectedLogger public partial class ProtectedLogger
{ {
// 生成 protected 字段 // 生成 protected 字段
} }
``` ```
### 配置选项说明 ### 配置选项说明
| 参数 | 类型 | 默认值 | 说明 | | 参数 | 类型 | 默认值 | 说明 |
|----------------|---------|-----------|---------------------------------| |----------------|---------|-----------|---------------------------------|
| Name | string? | null | 日志分类名称(默认使用类名) | | Name | string? | null | 日志分类名称(默认使用类名) |
| FieldName | string | "Logger" | 生成的字段名称 | | FieldName | string | "Logger" | 生成的字段名称 |
| IsStatic | bool | true | 是否生成静态字段 | | IsStatic | bool | true | 是否生成静态字段 |
| AccessModifier | string | "private" | 访问修饰符private/protected/public | | AccessModifier | string | "private" | 访问修饰符private/protected/public |
## 与其他模块集成 ## 与其他模块集成
### 与 Godot 集成 ### 与 Godot 集成
```csharp ```csharp
[Log] [Log]
[ContextAware] [ContextAware]
public partial class GodotController : Node public partial class GodotController : Node
{ {
public override void _Ready() public override void _Ready()
{ {
Logger.Info("控制器已准备就绪"); Logger.Info("控制器已准备就绪");
} }
} }
``` ```
### 与架构集成 ### 与架构集成
```csharp ```csharp
[Log] [Log]
public partial class MySystem : AbstractSystem public partial class MySystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
Logger.Info("系统初始化"); Logger.Info("系统初始化");
} }
} }
``` ```
## 实际应用示例 ## 实际应用示例
### 游戏控制器 ### 游戏控制器
```csharp ```csharp
[Log] using GFramework.Core.Abstractions.controller;
[ContextAware] using GFramework.SourceGenerators.Abstractions.logging;
public partial class PlayerController : IController using GFramework.SourceGenerators.Abstractions.rule;
{
public void HandleInput(string action) [Log]
{ [ContextAware]
Logger.Debug($"处理输入: {action}"); public partial class PlayerController : IController
{
switch (action) public void HandleInput(string action)
{ {
case "jump": Logger.Debug($"处理输入: {action}");
Logger.Info("玩家跳跃");
Jump(); switch (action)
break; {
case "attack": case "jump":
Logger.Info("玩家攻击"); Logger.Info("玩家跳跃");
Attack(); Jump();
break; break;
default: case "attack":
Logger.Warning($"未知操作: {action}"); Logger.Info("玩家攻击");
break; Attack();
} break;
} default:
Logger.Warning($"未知操作: {action}");
private void Jump() break;
{ }
try }
{
// 跳跃逻辑 private void Jump()
Logger.Debug("跳跃执行成功"); {
} try
catch (Exception ex) {
{ // 跳跃逻辑
Logger.Error($"跳跃失败: {ex.Message}"); Logger.Debug("跳跃执行成功");
} }
} catch (Exception ex)
} {
``` Logger.Error($"跳跃失败: {ex.Message}");
}
### 数据处理服务 }
}
```csharp ```
[Log("DataService")]
public partial class DataProcessor ### 数据处理服务
{
public void ProcessData(string data) ```csharp
{ [Log("DataService")]
Logger.Info($"开始处理数据,长度: {data.Length}"); public partial class DataProcessor
{
if (string.IsNullOrEmpty(data)) public void ProcessData(string data)
{ {
Logger.Warning("数据为空,跳过处理"); Logger.Info($"开始处理数据,长度: {data.Length}");
return;
} if (string.IsNullOrEmpty(data))
{
try Logger.Warning("数据为空,跳过处理");
{ return;
// 处理逻辑 }
Logger.Debug("数据处理中...");
// ... try
Logger.Info("数据处理完成"); {
} // 处理逻辑
catch (Exception ex) Logger.Debug("数据处理中...");
{ // ...
Logger.Error($"数据处理失败: {ex.Message}"); Logger.Info("数据处理完成");
throw; }
} catch (Exception ex)
} {
} Logger.Error($"数据处理失败: {ex.Message}");
``` throw;
}
## 最佳实践 }
}
### 1. 合理使用日志级别 ```
```csharp ## 最佳实践
[Log]
public partial class BestPracticeExample ### 1. 合理使用日志级别
{
public void ProcessRequest() ```csharp
{ [Log]
// Debug: 详细的调试信息 public partial class BestPracticeExample
Logger.Debug("开始处理请求"); {
public void ProcessRequest()
// Info: 重要的业务流程信息 {
Logger.Info("请求处理成功"); // Debug: 详细的调试信息
Logger.Debug("开始处理请求");
// Warning: 可恢复的异常情况
Logger.Warning("缓存未命中,使用默认值"); // Info: 重要的业务流程信息
Logger.Info("请求处理成功");
// Error: 错误但不影响系统运行
Logger.Error("处理失败,将重试"); // Warning: 可恢复的异常情况
Logger.Warning("缓存未命中,使用默认值");
// Critical: 严重错误,可能导致系统崩溃
Logger.Critical("数据库连接失败"); // Error: 错误但不影响系统运行
} Logger.Error("处理失败,将重试");
}
``` // Critical: 严重错误,可能导致系统崩溃
Logger.Critical("数据库连接失败");
### 2. 避免过度日志 }
}
```csharp ```
[Log]
public partial class PerformanceExample ### 2. 避免过度日志
{
private int _frameCount = 0; ```csharp
[Log]
public void Update() public partial class PerformanceExample
{ {
// 好的做法:定期记录 private int _frameCount = 0;
if (_frameCount % 1000 == 0)
{ public void Update()
Logger.Debug($"已运行 {_frameCount} 帧"); {
} // 好的做法:定期记录
_frameCount++; if (_frameCount % 1000 == 0)
{
// 避免:每帧都记录 Logger.Debug($"已运行 {_frameCount} 帧");
// Logger.Debug($"帧 {_frameCount}"); // ❌ 太频繁 }
} _frameCount++;
}
``` // 避免:每帧都记录
// Logger.Debug($"帧 {_frameCount}"); // ❌ 太频繁
### 3. 结构化日志信息 }
}
```csharp ```
[Log]
public partial class StructuredLogging ### 3. 结构化日志信息
{
public void ProcessUser(int userId, string action) ```csharp
{ [Log]
// 好的做法:包含上下文信息 public partial class StructuredLogging
Logger.Info($"用户操作 [UserId={userId}, Action={action}]"); {
public void ProcessUser(int userId, string action)
// 避免:信息不完整 {
// Logger.Info("用户操作"); // ❌ 缺少上下文 // 好的做法:包含上下文信息
} Logger.Info($"用户操作 [UserId={userId}, Action={action}]");
}
``` // 避免:信息不完整
// Logger.Info("用户操作"); // ❌ 缺少上下文
## 常见问题 }
}
### Q: 为什么需要 partial 关键字? ```
**A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。 ## 常见问题
### Q: 可以在静态类中使用吗? ### Q: 为什么需要 partial 关键字?
**A**: 可以,生成器会自动生成静态字段: **A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。
```csharp ### Q: 可以在静态类中使用吗?
[Log]
public static partial class StaticHelper **A**: 可以,生成器会自动生成静态字段:
{
public static void DoSomething() ```csharp
{ [Log]
Logger.Info("静态方法日志"); public static partial class StaticHelper
} {
} public static void DoSomething()
``` {
Logger.Info("静态方法日志");
### Q: 如何自定义日志工厂? }
}
**A**: 通过配置 `LoggerFactoryResolver.Provider` 来自定义日志工厂实现。 ```
--- ### Q: 如何自定义日志工厂?
**相关文档** **A**: 通过配置 `LoggerFactoryResolver.Provider` 来自定义日志工厂实现。
- [Source Generators 概述](./index) ---
- [枚举扩展生成器](./enum-generator)
- [ContextAware 生成器](./context-aware-generator) **相关文档**
- [Source Generators 概述](./index)
- [枚举扩展生成器](./enum-generator)
- [ContextAware 生成器](./context-aware-generator)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff