diff --git a/GFramework.Core.Abstractions/rule/IContextAware.cs b/GFramework.Core.Abstractions/rule/IContextAware.cs index 01cac5e..9fb8e6a 100644 --- a/GFramework.Core.Abstractions/rule/IContextAware.cs +++ b/GFramework.Core.Abstractions/rule/IContextAware.cs @@ -3,7 +3,7 @@ namespace GFramework.Core.Abstractions.rule; /// -/// 上下文感知接口,用于为实现该接口的类提供设置架构上下文的能力 +/// 上下文感知接口,用于为实现该接口的类提供设置和获取架构上下文的能力 /// public interface IContextAware { @@ -12,4 +12,10 @@ public interface IContextAware /// /// 架构上下文对象,用于提供架构级别的服务和功能访问 void SetContext(IArchitectureContext context); + + /// + /// 获取架构上下文 + /// + /// 当前的架构上下文对象 + IArchitectureContext GetContext(); } \ No newline at end of file diff --git a/GFramework.Core/extensions/ContextAwareExtensions.cs b/GFramework.Core/extensions/ContextAwareExtensions.cs new file mode 100644 index 0000000..3a5af4b --- /dev/null +++ b/GFramework.Core/extensions/ContextAwareExtensions.cs @@ -0,0 +1,166 @@ +using GFramework.Core.Abstractions.command; +using GFramework.Core.Abstractions.events; +using GFramework.Core.Abstractions.model; +using GFramework.Core.Abstractions.query; +using GFramework.Core.Abstractions.rule; +using GFramework.Core.Abstractions.system; +using GFramework.Core.Abstractions.utility; + +namespace GFramework.Core.extensions; + +/// +/// 提供对 IContextAware 接口的扩展方法 +/// +public static class ContextAwareExtensions +{ + /// + /// 获取架构上下文中的指定系统 + /// + /// 目标系统类型 + /// 实现 IContextAware 接口的对象 + /// 指定类型的系统实例 + /// 当 contextAware 为 null 时抛出 + public static TSystem? GetSystem(this IContextAware contextAware) where TSystem : class, ISystem + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + var context = contextAware.GetContext(); + return context.GetSystem(); + } + + /// + /// 获取架构上下文中的指定模型 + /// + /// 目标模型类型 + /// 实现 IContextAware 接口的对象 + /// 指定类型的模型实例 + /// 当 contextAware 为 null 时抛出 + public static TModel? GetModel(this IContextAware contextAware) where TModel : class, IModel + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + var context = contextAware.GetContext(); + return context.GetModel(); + } + + /// + /// 获取架构上下文中的指定工具 + /// + /// 目标工具类型 + /// 实现 IContextAware 接口的对象 + /// 指定类型的工具实例 + /// 当 contextAware 为 null 时抛出 + public static TUtility? GetUtility(this IContextAware contextAware) where TUtility : class, IUtility + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + var context = contextAware.GetContext(); + return context.GetUtility(); + } + + /// + /// 发送一个查询请求 + /// + /// 查询结果类型 + /// 实现 IContextAware 接口的对象 + /// 要发送的查询 + /// 查询结果 + /// 当 contextAware 或 query 为 null 时抛出 + public static TResult SendQuery(this IContextAware contextAware, IQuery query) + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + if (query == null) throw new ArgumentNullException(nameof(query)); + + var context = contextAware.GetContext(); + return context.SendQuery(query); + } + + /// + /// 发送一个无返回结果的命令 + /// + /// 实现 IContextAware 接口的对象 + /// 要发送的命令 + /// 当 contextAware 或 command 为 null 时抛出 + public static void SendCommand(this IContextAware contextAware, ICommand command) + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + if (command == null) throw new ArgumentNullException(nameof(command)); + + var context = contextAware.GetContext(); + context.SendCommand(command); + } + + /// + /// 发送一个带返回值的命令 + /// + /// 命令执行结果类型 + /// 实现 IContextAware 接口的对象 + /// 要发送的命令 + /// 命令执行结果 + /// 当 contextAware 或 command 为 null 时抛出 + public static TResult SendCommand(this IContextAware contextAware, ICommand command) + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + if (command == null) throw new ArgumentNullException(nameof(command)); + + var context = contextAware.GetContext(); + return context.SendCommand(command); + } + + /// + /// 发送一个事件 + /// + /// 事件类型 + /// 实现 IContextAware 接口的对象 + /// 当 contextAware 为 null 时抛出 + public static void SendEvent(this IContextAware contextAware) where TEvent : new() + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + var context = contextAware.GetContext(); + context.SendEvent(); + } + + /// + /// 发送一个具体的事件实例 + /// + /// 事件类型 + /// 实现 IContextAware 接口的对象 + /// 事件实例 + /// 当 contextAware 或 e 为 null 时抛出 + public static void SendEvent(this IContextAware contextAware, TEvent e) where TEvent : class + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + if (e == null) throw new ArgumentNullException(nameof(e)); + + var context = contextAware.GetContext(); + context.SendEvent(e); + } + + /// + /// 注册事件处理器 + /// + /// 事件类型 + /// 实现 IContextAware 接口的对象 + /// 事件处理委托 + /// 事件注销接口 + public static IUnRegister RegisterEvent(this IContextAware contextAware, Action handler) + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + if (handler == null) throw new ArgumentNullException(nameof(handler)); + + var context = contextAware.GetContext(); + return context.RegisterEvent(handler); + } + + /// + /// 取消对某类型事件的监听 + /// + /// 事件类型 + /// 实现 IContextAware 接口的对象 + /// 之前绑定的事件处理器 + public static void UnRegisterEvent(this IContextAware contextAware, Action onEvent) + { + if (contextAware == null) throw new ArgumentNullException(nameof(contextAware)); + if (onEvent == null) throw new ArgumentNullException(nameof(onEvent)); + + var context = contextAware.GetContext(); + context.UnRegisterEvent(onEvent); + } +} \ No newline at end of file diff --git a/GFramework.Core/rule/ContextAwareBase.cs b/GFramework.Core/rule/ContextAwareBase.cs index 09a8de2..aca31d0 100644 --- a/GFramework.Core/rule/ContextAwareBase.cs +++ b/GFramework.Core/rule/ContextAwareBase.cs @@ -23,6 +23,15 @@ public abstract class ContextAwareBase : IContextAware OnContextReady(); } + /// + /// 获取架构上下文 + /// + /// 当前架构上下文对象 + IArchitectureContext IContextAware.GetContext() + { + return Context; + } + /// /// 当上下文准备就绪时调用的虚方法,子类可以重写此方法来执行上下文相关的初始化逻辑 /// diff --git a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs index 78480da..684d3f9 100644 --- a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs +++ b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs @@ -20,12 +20,18 @@ public sealed class GodotLoggerGenerator : TypeAttributeClassGeneratorBase protected override string AttributeShortNameWithoutSuffix => "GodotLog"; /// - /// 生成日志字段的源代码 + /// 生成源代码 /// - /// 类符号 - /// GodotLogAttribute属性数据 + /// 源生产上下文 + /// 编译对象 + /// 命名类型符号 + /// 属性数据 /// 生成的源代码字符串 - protected override string Generate(INamedTypeSymbol symbol, AttributeData attr) + protected override string Generate( + SourceProductionContext context, + Compilation compilation, + INamedTypeSymbol symbol, + AttributeData attr) { var ns = symbol.ContainingNamespace.IsGlobalNamespace ? null diff --git a/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs b/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs index c3461d0..bc85920 100644 --- a/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs +++ b/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs @@ -109,7 +109,7 @@ public abstract class AttributeClassGeneratorBase : IIncrementalGenerator CommonDiagnostics.Trace(context, $"[GEN] Generating source: {hintName}"); - context.AddSource(hintName, Generate(symbol, attr)); + context.AddSource(hintName, Generate(context, compilation, symbol, attr)); } @@ -133,10 +133,14 @@ public abstract class AttributeClassGeneratorBase : IIncrementalGenerator /// /// 生成源代码 /// + /// 源生产上下文 + /// 编译对象 /// 命名类型符号 /// 属性数据 /// 生成的源代码字符串 protected abstract string Generate( + SourceProductionContext context, + Compilation compilation, INamedTypeSymbol symbol, AttributeData attr); diff --git a/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs b/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs index 971c18d..3c36dd7 100644 --- a/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs +++ b/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs @@ -40,6 +40,8 @@ public class ContextAwareGeneratorTests { void SetContext( GFramework.Core.Abstractions.architecture.IArchitectureContext context); + + GFramework.Core.Abstractions.architecture.IArchitectureContext GetContext(); } } @@ -62,11 +64,17 @@ public class ContextAwareGeneratorTests /// protected GFramework.Core.Abstractions.architecture.IArchitectureContext Context { get; private set; } = null!; - void GFramework.Core.Abstractions.rule.IContextAware.SetContext( - GFramework.Core.Abstractions.architecture.IArchitectureContext context) + 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; + } } """; @@ -113,6 +121,8 @@ public class ContextAwareGeneratorTests { void SetContext( GFramework.Core.Abstractions.architecture.IArchitectureContext context); + + GFramework.Core.Abstractions.architecture.IArchitectureContext GetContext(); } } @@ -140,6 +150,12 @@ public class ContextAwareGeneratorTests { Context = context; } + + GFramework.Core.Abstractions.architecture.IArchitectureContext + GFramework.Core.Abstractions.rule.IContextAware.GetContext() + { + return Context; + } } """; diff --git a/GFramework.SourceGenerators/logging/LoggerGenerator.cs b/GFramework.SourceGenerators/logging/LoggerGenerator.cs index f8c1292..add02af 100644 --- a/GFramework.SourceGenerators/logging/LoggerGenerator.cs +++ b/GFramework.SourceGenerators/logging/LoggerGenerator.cs @@ -41,9 +41,18 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase } /// - /// 生成 Logger 字段的代码 + /// 生成源代码 /// - protected override string Generate(INamedTypeSymbol symbol, AttributeData attr) + /// 源生产上下文 + /// 编译对象 + /// 命名类型符号 + /// 属性数据 + /// 生成的源代码字符串 + protected override string Generate( + SourceProductionContext context, + Compilation compilation, + INamedTypeSymbol symbol, + AttributeData attr) { var ns = symbol.ContainingNamespace.IsGlobalNamespace ? null diff --git a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs index b8fd5a7..5d825b4 100644 --- a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs +++ b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs @@ -11,23 +11,11 @@ namespace GFramework.SourceGenerators.rule; [Generator] public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase { - /// - /// 获取属性元数据的完整名称,用于标识ContextAwareAttribute的完全限定名 - /// - /// 返回ContextAwareAttribute的完全限定名字符串 protected override string AttributeMetadataName => $"{PathContests.SourceGeneratorsAbstractionsPath}.rule.ContextAwareAttribute"; - /// - /// 仅用于 Syntax 粗筛选 - /// - /// 返回属性的简短名称,不包含后缀 protected override string AttributeShortNameWithoutSuffix => "ContextAware"; - - /// - /// 额外语义校验:必须实现 IContextAware - /// protected override bool ValidateSymbol( SourceProductionContext context, Compilation compilation, @@ -35,9 +23,8 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase INamedTypeSymbol symbol, AttributeData attr) { - var iContextAware = compilation - .GetTypeByMetadataName( - $"{PathContests.CoreAbstractionsNamespace}.rule.IContextAware"); + var iContextAware = compilation.GetTypeByMetadataName( + $"{PathContests.CoreAbstractionsNamespace}.rule.IContextAware"); if (iContextAware is null) { @@ -45,7 +32,6 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase ContextAwareDiagnostic.ClassMustImplementIContextAware, syntax.Identifier.GetLocation(), symbol.Name)); - return false; } @@ -56,24 +42,31 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase ContextAwareDiagnostic.ClassMustImplementIContextAware, syntax.Identifier.GetLocation(), symbol.Name)); - return false; } return true; } - /// - /// 生成源码 + /// 生成源代码 /// + /// 源生产上下文 + /// 编译对象 + /// 命名类型符号 + /// 属性数据 + /// 生成的源代码字符串 protected override string Generate( + SourceProductionContext context, + Compilation compilation, INamedTypeSymbol symbol, AttributeData attr) { var ns = symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToDisplayString(); + var iContextAware = compilation.GetTypeByMetadataName( + $"{PathContests.CoreAbstractionsNamespace}.rule.IContextAware"); var sb = new StringBuilder(); sb.AppendLine("// "); @@ -89,36 +82,106 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase sb.AppendLine($"partial class {symbol.Name}"); sb.AppendLine("{"); - // 属性 + GenerateContextProperty(sb); + GenerateInterfaceImplementations(sb, iContextAware!); + + sb.AppendLine("}"); + return sb.ToString().TrimEnd(); + } + + protected override string GetHintName(INamedTypeSymbol symbol) + => $"{symbol.Name}.ContextAware.g.cs"; + + // ========================= + // 生成 Context 属性 + // ========================= + private static void GenerateContextProperty(StringBuilder sb) + { sb.AppendLine(" /// "); sb.AppendLine(" /// 自动注入的架构上下文"); sb.AppendLine(" /// "); sb.AppendLine( $" protected {PathContests.CoreAbstractionsNamespace}.architecture.IArchitectureContext Context {{ get; private set; }} = null!;"); sb.AppendLine(); - - // 方法 - sb.AppendLine( - $" void {PathContests.CoreAbstractionsNamespace}.rule.IContextAware.SetContext("); - sb.AppendLine( - $" {PathContests.CoreAbstractionsNamespace}.architecture.IArchitectureContext context)"); - sb.AppendLine(" {"); - sb.AppendLine(" Context = context;"); - sb.AppendLine(" }"); - - sb.AppendLine("}"); - - return sb.ToString().TrimEnd(); } - - /// - /// 自定义生成文件名 - /// - protected override string GetHintName(INamedTypeSymbol symbol) + // ========================= + // 自动实现接口方法 + // ========================= + private static void GenerateInterfaceImplementations( + StringBuilder sb, + INamedTypeSymbol interfaceSymbol) { - // 包含命名空间和生成器类名路径 - return - $"{symbol.Name}.ContextAware.g.cs"; + var interfaceFullName = interfaceSymbol.ToDisplayString( + SymbolDisplayFormat.FullyQualifiedFormat); + + foreach (var member in interfaceSymbol.GetMembers().OfType()) + { + if (member.MethodKind != MethodKind.Ordinary) + continue; + + GenerateMethod(sb, interfaceFullName, member); + sb.AppendLine(); + } + } + + private static void GenerateMethod( + StringBuilder sb, + string interfaceFullName, + IMethodSymbol method) + { + var returnType = method.ReturnType.ToDisplayString( + SymbolDisplayFormat.FullyQualifiedFormat); + + var parameters = string.Join(", ", + method.Parameters.Select(p => + $"{p.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {p.Name}")); + + sb.AppendLine( + $" {returnType} {interfaceFullName}.{method.Name}({parameters})"); + + sb.AppendLine(" {"); + GenerateMethodBody(sb, method); + sb.AppendLine(" }"); + } + + // ========================= + // 方法语义策略 + // ========================= + private static void GenerateMethodBody( + StringBuilder sb, + IMethodSymbol method) + { + switch (method.Name) + { + case "SetContext": + sb.AppendLine(" Context = context;"); + break; + + case "GetContext": + sb.AppendLine(" return Context;"); + break; + + default: + GenerateFallbackBody(sb, method); + break; + } + } + + private static void GenerateFallbackBody( + StringBuilder sb, + IMethodSymbol method) + { + if (method.ReturnsVoid) + { + sb.AppendLine(" // no-op"); + } + else + { + sb.AppendLine( + $" throw new System.NotImplementedException("); + sb.AppendLine( + $" \"Method '{method.Name}' is not supported by ContextAwareGenerator.\");"); + } } } \ No newline at end of file