mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +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>()
11 KiB
11 KiB
ContextAware 生成器
自动实现 IContextAware 接口,提供架构上下文访问能力
概述
ContextAware 生成器为标记了 [ContextAware] 属性的类自动生成 IContextAware 接口实现,使类能够便捷地访问架构上下文(
IArchitectureContext)。这是 GFramework 中最常用的源码生成器之一,几乎所有需要与架构交互的组件都会使用它。
核心功能
- 自动接口实现:无需手动实现
IContextAware接口的SetContext()和GetContext()方法 - 懒加载上下文:
Context属性在首次访问时自动初始化 - 默认提供者:使用
GameContextProvider作为默认上下文提供者 - 测试友好:支持通过
SetContextProvider()配置自定义上下文提供者
基础使用
标记类
使用 [ContextAware] 属性标记需要访问架构上下文的类:
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));
}
}
必要条件
标记的类必须满足以下条件:
- 必须是
partial类:生成器需要生成部分类代码 - 必须是
class类型:不能是struct或interface
// ✅ 正确
[ContextAware]
public partial class MyController { }
// ❌ 错误:缺少 partial 关键字
[ContextAware]
public class MyController { }
// ❌ 错误:不能用于 struct
[ContextAware]
public partial struct MyStruct { }
生成的代码
编译器会为标记的类自动生成以下代码:
// <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;
}
}
代码解析
生成的代码包含以下关键部分:
-
私有字段:
_context:缓存的上下文实例_contextProvider:静态上下文提供者(所有实例共享)
-
Context 属性:
protected访问级别,子类可访问- 懒加载:首次访问时自动初始化
- 使用
GameContextProvider作为默认提供者
-
配置方法:
SetContextProvider():设置自定义上下文提供者ResetContextProvider():重置为默认提供者
-
显式接口实现:
IContextAware.SetContext():允许外部设置上下文IContextAware.GetContext():返回当前上下文
配置上下文提供者
测试场景
在单元测试中,通常需要使用自定义的上下文提供者:
[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();
}
}
多架构场景
在某些高级场景中,可能需要同时运行多个架构实例:
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] 属性:
- Controller 层:需要协调多个 Model/System 的控制器
- Command/Query 实现:需要访问架构服务的命令或查询
- 自定义组件:不继承框架基类但需要上下文访问的组件
[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]:
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:
// 推荐:需要生命周期管理时继承基类
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 接口:
namespace GFramework.Core.Abstractions.rule;
public interface IContextAware
{
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
}
这意味着标记了 [ContextAware] 的类可以:
- 被架构自动注入上下文:实现
IContextAware的类在注册到架构时会自动调用SetContext() - 参与依赖注入:可以作为
IContextAware类型注入到其他组件 - 支持上下文传递:可以通过
GetContext()将上下文传递给其他组件
最佳实践
1. 始终使用 partial 关键字
// ✅ 正确
[ContextAware]
public partial class MyController { }
// ❌ 错误:编译器会报错
[ContextAware]
public class MyController { }
2. 在测试中清理上下文提供者
[TearDown]
public void TearDown()
{
// 避免测试之间的状态污染
PlayerController.ResetContextProvider();
EnemyController.ResetContextProvider();
}
3. 避免在构造函数中访问 Context
[ContextAware]
public partial class MyController
{
// ❌ 错误:构造函数执行时上下文可能未初始化
public MyController()
{
var model = this.GetModel<SomeModel>(); // 可能为 null
}
// ✅ 正确:在初始化方法中访问
public void Initialize()
{
var model = this.GetModel<SomeModel>(); // 安全
}
}
4. 优先使用 Context 属性而非接口方法
[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
[ContextAware]
public class MyController { } // 错误:缺少 partial 关键字
解决方案:添加 partial 关键字
[ContextAware]
public partial class MyController { } // ✅ 正确
GFSG002: ContextAware 只能用于类
[ContextAware]
public partial struct MyStruct { } // 错误:不能用于 struct
解决方案:将 struct 改为 class
[ContextAware]
public partial class MyClass { } // ✅ 正确