mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(architecture): 添加架构上下文管理和绑定功能
- 在Architecture类中添加GameContext.Bind调用以绑定架构上下文 - 创建GameContext类用于管理架构上下文实例的注册和获取 - 实现架构上下文的绑定、获取、查找和移除功能 - 更新ContextAwareGenerator生成器以使用懒加载方式获取上下文 - 在测试架构中添加就绪事件注册功能 - 添加架构上下文按类型注册的测试用例
This commit is contained in:
parent
56ff201f94
commit
0be919d8b1
@ -1,4 +1,5 @@
|
||||
using GFramework.Core.architecture;
|
||||
using GFramework.Core.events;
|
||||
using GFramework.Core.Tests.model;
|
||||
using GFramework.Core.Tests.system;
|
||||
|
||||
@ -6,6 +7,7 @@ namespace GFramework.Core.Tests.architecture;
|
||||
|
||||
public sealed class TestArchitecture : Architecture
|
||||
{
|
||||
public bool ReadyEventFired { get; private set; }
|
||||
public bool InitCalled { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
@ -14,5 +16,6 @@ public sealed class TestArchitecture : Architecture
|
||||
|
||||
RegisterModel(new TestModel());
|
||||
RegisterSystem(new TestSystem());
|
||||
Context.RegisterEvent<ArchitectureEvents.ArchitectureLifecycleReadyEvent>(_ => { ReadyEventFired = true; });
|
||||
}
|
||||
}
|
||||
@ -54,4 +54,14 @@ public class ArchitectureInitializationTests
|
||||
Assert.That(system, Is.Not.Null);
|
||||
Assert.That(system!.Inited, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Architecture_Should_Register_Context_By_Type()
|
||||
{
|
||||
// Act
|
||||
_architecture!.Initialize();
|
||||
var ctx = GameContext.GetByType(_architecture!.GetType());
|
||||
|
||||
Assert.That(ctx, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
@ -246,7 +246,7 @@ public abstract class Architecture(
|
||||
{
|
||||
_logger = Configuration.LoggerFactory.GetLogger(GetType().Name);
|
||||
_context ??= new ArchitectureContext(Container, TypeEventSystem, Configuration.LoggerFactory);
|
||||
|
||||
GameContext.Bind(GetType(), _context);
|
||||
// 创建架构运行时实例
|
||||
Runtime = new ArchitectureRuntime(_context);
|
||||
((ArchitectureContext)_context).Runtime = Runtime;
|
||||
@ -299,7 +299,7 @@ public abstract class Architecture(
|
||||
{
|
||||
_logger = Configuration.LoggerFactory.GetLogger(GetType().Name);
|
||||
_context ??= new ArchitectureContext(Container, TypeEventSystem, Configuration.LoggerFactory);
|
||||
|
||||
GameContext.Bind(GetType(), _context);
|
||||
// 创建架构运行时实例
|
||||
Runtime = new ArchitectureRuntime(_context);
|
||||
((ArchitectureContext)_context).Runtime = Runtime;
|
||||
|
||||
113
GFramework.Core/architecture/GameContext.cs
Normal file
113
GFramework.Core/architecture/GameContext.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System.Collections.Concurrent;
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
|
||||
namespace GFramework.Core.architecture;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏上下文管理类,用于管理当前的架构上下文实例
|
||||
/// </summary>
|
||||
public static class GameContext
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, IArchitectureContext> ArchitectureDictionary
|
||||
= new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已注册的架构上下文的只读字典
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<Type, IArchitectureContext> ArchitectureReadOnlyDictionary =>
|
||||
ArchitectureDictionary;
|
||||
|
||||
/// <summary>
|
||||
/// 绑定指定类型的架构上下文到管理器中
|
||||
/// </summary>
|
||||
/// <param name="architectureType">架构类型</param>
|
||||
/// <param name="context">架构上下文实例</param>
|
||||
/// <exception cref="InvalidOperationException">当指定类型的架构上下文已存在时抛出</exception>
|
||||
public static void Bind(Type architectureType, IArchitectureContext context)
|
||||
{
|
||||
if (!ArchitectureDictionary.TryAdd(architectureType, context))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Architecture context for '{architectureType.Name}' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字典中的第一个架构上下文
|
||||
/// </summary>
|
||||
/// <returns>返回字典中的第一个架构上下文实例</returns>
|
||||
/// <exception cref="InvalidOperationException">当字典为空时抛出</exception>
|
||||
public static IArchitectureContext GetFirstArchitecture()
|
||||
{
|
||||
return ArchitectureDictionary.Values.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据类型获取对应的架构上下文
|
||||
/// </summary>
|
||||
/// <param name="type">要查找的架构类型</param>
|
||||
/// <returns>返回指定类型的架构上下文实例</returns>
|
||||
/// <exception cref="InvalidOperationException">当指定类型的架构上下文不存在时抛出</exception>
|
||||
public static IArchitectureContext GetByType(Type type)
|
||||
{
|
||||
if (ArchitectureDictionary.TryGetValue(type, out var context))
|
||||
return context;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Architecture context for '{type.Name}' not found");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的架构上下文实例
|
||||
/// </summary>
|
||||
/// <typeparam name="T">架构上下文类型,必须实现IArchitectureContext接口</typeparam>
|
||||
/// <returns>指定类型的架构上下文实例</returns>
|
||||
/// <exception cref="InvalidOperationException">当指定类型的架构上下文不存在时抛出</exception>
|
||||
public static T Get<T>() where T : class, IArchitectureContext
|
||||
{
|
||||
if (ArchitectureDictionary.TryGetValue(typeof(T), out var ctx))
|
||||
return (T)ctx;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Architecture context '{typeof(T).Name}' not found");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取指定类型的架构上下文实例
|
||||
/// </summary>
|
||||
/// <typeparam name="T">架构上下文类型,必须实现IArchitectureContext接口</typeparam>
|
||||
/// <param name="context">输出参数,如果找到则返回对应的架构上下文实例,否则返回null</param>
|
||||
/// <returns>如果找到指定类型的架构上下文则返回true,否则返回false</returns>
|
||||
public static bool TryGet<T>(out T? context)
|
||||
where T : class, IArchitectureContext
|
||||
{
|
||||
if (ArchitectureDictionary.TryGetValue(typeof(T), out var ctx))
|
||||
{
|
||||
context = (T)ctx;
|
||||
return true;
|
||||
}
|
||||
|
||||
context = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定类型的架构上下文绑定
|
||||
/// </summary>
|
||||
/// <param name="architectureType">要移除的架构类型</param>
|
||||
public static void Unbind(Type architectureType)
|
||||
{
|
||||
ArchitectureDictionary.TryRemove(architectureType, out _);
|
||||
}
|
||||
|
||||
// 测试专用
|
||||
/// <summary>
|
||||
/// 为测试重置所有架构上下文(仅内部使用)
|
||||
/// </summary>
|
||||
internal static void ResetForTests()
|
||||
{
|
||||
ArchitectureDictionary.Clear();
|
||||
}
|
||||
}
|
||||
@ -55,6 +55,21 @@ public class ContextAwareGeneratorSnapshotTests
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace GFramework.Core.architecture
|
||||
{
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
public class GameContext{
|
||||
/// <summary>
|
||||
/// 获取字典中的第一个架构上下文
|
||||
/// </summary>
|
||||
/// <returns>返回字典中的第一个架构上下文实例</returns>
|
||||
/// <exception cref="InvalidOperationException">当字典为空时抛出</exception>
|
||||
public static IArchitectureContext GetFirstArchitecture()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
// 执行生成器快照测试,将生成的代码与预期快照进行比较
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
using GFramework.SourceGenerators.rule;
|
||||
using GFramework.SourceGenerators.Tests.core;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.rule;
|
||||
|
||||
[TestFixture]
|
||||
public class ContextAwareGeneratorTests
|
||||
{
|
||||
[Test]
|
||||
public async Task Generates_ContextAware_Code_When_Interface_Inherits_IContextAware()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class ContextAwareAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.rule;
|
||||
using GFramework.Core.Abstractions.rule;
|
||||
|
||||
public interface IMyRuleContextAware : IContextAware
|
||||
{
|
||||
}
|
||||
|
||||
[ContextAware]
|
||||
public partial class MyRule : IMyRuleContextAware
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string frameworkStub = """
|
||||
namespace GFramework.Core.Abstractions.rule
|
||||
{
|
||||
public interface IContextAware
|
||||
{
|
||||
void SetContext(
|
||||
GFramework.Core.Abstractions.architecture.IArchitectureContext context);
|
||||
|
||||
GFramework.Core.Abstractions.architecture.IArchitectureContext GetContext();
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.architecture
|
||||
{
|
||||
public interface IArchitectureContext {}
|
||||
}
|
||||
""";
|
||||
|
||||
const string expected = """
|
||||
// <auto-generated/>
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class MyRule
|
||||
{
|
||||
/// <summary>
|
||||
/// 自动注入的架构上下文
|
||||
/// </summary>
|
||||
protected GFramework.Core.Abstractions.architecture.IArchitectureContext Context { get; private set; } = 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;
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
await GeneratorTest<ContextAwareGenerator>.RunAsync(
|
||||
source + "\n" + frameworkStub,
|
||||
("MyRule.ContextAware.g.cs", expected)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -117,14 +117,28 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
|
||||
/// <param name="sb">字符串构建器</param>
|
||||
private static void GenerateContextProperty(StringBuilder sb)
|
||||
{
|
||||
sb.AppendLine(" private global::GFramework.Core.Abstractions.architecture.IArchitectureContext? _context;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" /// <summary>");
|
||||
sb.AppendLine(" /// 自动注入的架构上下文");
|
||||
sb.AppendLine(" /// 自动获取的架构上下文(懒加载,默认使用第一个架构)");
|
||||
sb.AppendLine(" /// </summary>");
|
||||
sb.AppendLine(" protected global::GFramework.Core.Abstractions.architecture.IArchitectureContext Context");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" get");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" if (_context == null)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(
|
||||
$" protected {PathContests.CoreAbstractionsNamespace}.architecture.IArchitectureContext Context {{ get; private set; }} = null!;");
|
||||
" _context = global::GFramework.Core.architecture.GameContext.GetFirstArchitecture();");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" return _context;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
|
||||
// =========================
|
||||
// 显式接口实现(使用 global::)
|
||||
// =========================
|
||||
@ -189,7 +203,7 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
|
||||
switch (method.Name)
|
||||
{
|
||||
case "SetContext":
|
||||
sb.AppendLine(" Context = context;");
|
||||
sb.AppendLine(" _context = context;");
|
||||
break;
|
||||
|
||||
case "GetContext":
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user