Skip to content
Merged
3 changes: 3 additions & 0 deletions GFramework.Core.SourceGenerators/Bases/PriorityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ protected override string Generate(
? $"<{string.Join(", ", symbol.TypeParameters.Select(tp => tp.Name))}>"
: string.Empty;

sb.AppendLine("/// <summary>");
sb.AppendLine("/// 为当前分部类型补充自动生成的优先级契约实现。");
sb.AppendLine("/// </summary>");
sb.AppendLine(
$"partial class {symbol.Name}{typeParameters} : global::GFramework.Core.Abstractions.Bases.IPrioritized");
sb.AppendLine("{");
Expand Down
20 changes: 18 additions & 2 deletions GFramework.Core.SourceGenerators/Enums/EnumExtensionsGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)

sb.AppendLine("{");

sb.AppendLine(" /// <summary>");
sb.AppendLine($" /// 为 <see cref=\"{fullEnumName}\" /> 提供自动生成的扩展方法。");
sb.AppendLine(" /// </summary>");
sb.AppendLine($" public static partial class {enumName}Extensions");
sb.AppendLine(" {");

Expand Down Expand Up @@ -176,7 +179,13 @@ private static bool AppendIsMethods(StringBuilder builder, IEnumerable<IFieldSym
builder.AppendLine();
}

builder.AppendLine($" /// <summary>是否为 {memberName}</summary>");
builder.AppendLine(" /// <summary>");
builder.AppendLine(
$" /// 判断给定值是否为 <see cref=\"{fullEnumName}.{memberName}\" />。");
builder.AppendLine(" /// </summary>");
builder.AppendLine(" /// <param name=\"value\">要检查的枚举值。</param>");
builder.AppendLine(
$" /// <returns>当 <paramref name=\"value\" /> 等于 <see cref=\"{fullEnumName}.{memberName}\" /> 时返回 <see langword=\"true\" />;否则返回 <see langword=\"false\" />。</returns>");
builder.AppendLine(
$" public static bool Is{memberName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};");
hasGeneratedMembers = true;
Expand All @@ -192,7 +201,14 @@ private static bool AppendIsMethods(StringBuilder builder, IEnumerable<IFieldSym
/// <param name="fullEnumName">枚举的完整类型名。</param>
private static void AppendIsInMethod(StringBuilder builder, string fullEnumName)
{
builder.AppendLine(" /// <summary>判断是否属于指定集合</summary>");
builder.AppendLine(" /// <summary>");
builder.AppendLine(" /// 判断给定值是否属于指定候选集合。");
builder.AppendLine(" /// </summary>");
builder.AppendLine(" /// <param name=\"value\">要检查的枚举值。</param>");
builder.AppendLine(
" /// <param name=\"values\">用于匹配的候选枚举值集合;当为 <see langword=\"null\" /> 时返回 <see langword=\"false\" />。</param>");
builder.AppendLine(
" /// <returns>当 <paramref name=\"value\" /> 命中任一候选值时返回 <see langword=\"true\" />;否则返回 <see langword=\"false\" />。</returns>");
builder.AppendLine(
$" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
builder.AppendLine(" {");
Expand Down
7 changes: 6 additions & 1 deletion GFramework.Core.SourceGenerators/Logging/LoggerGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,18 @@ protected override string Generate(
.AppendLine($"namespace {ns};");

sb.AppendLine()
.AppendLine("/// <summary>")
.AppendLine("/// 为当前分部类型提供自动生成的日志字段。")
.AppendLine("/// </summary>")
.AppendLine($"partial {typeKind} {className}{generics.Parameters}");

foreach (var c in generics.Constraints)
sb.AppendLine($" {c}");

sb.AppendLine("{")
.AppendLine(" /// <summary>Auto-generated logger</summary>")
.AppendLine(" /// <summary>")
.AppendLine(" /// 自动生成的日志字段。")
.AppendLine(" /// </summary>")
.AppendLine(
$" {access} {staticKeyword}readonly ILogger {fieldName} = " +
$"LoggerFactoryResolver.Provider.CreateLogger(\"{logName}\");")
Expand Down
82 changes: 71 additions & 11 deletions GFramework.Core.SourceGenerators/Rule/ContextAwareGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ protected override string Generate(

var interfaceName = iContextAware.ToDisplayString(
SymbolDisplayFormat.FullyQualifiedFormat);
sb.AppendLine("/// <summary>");
sb.AppendLine("/// 为当前规则类型补充自动生成的架构上下文访问实现。");
sb.AppendLine("/// </summary>");
sb.AppendLine("/// <remarks>");
sb.AppendLine(
"/// 生成代码会在实例级缓存首次解析到的上下文,并在未显式配置提供者时回退到 <see cref=\"GFramework.Core.Architectures.GameContextProvider\" />。");
sb.AppendLine(
"/// 同一生成类型的所有实例共享一个静态上下文提供者;切换或重置提供者只会影响尚未缓存上下文的新实例或未初始化实例,");
sb.AppendLine(
"/// 已缓存的实例上下文需要通过 <see cref=\"GFramework.Core.Abstractions.Rule.IContextAware.SetContext(GFramework.Core.Abstractions.Architectures.IArchitectureContext)\" /> 显式覆盖。");
sb.AppendLine(
"/// 与手动继承 <see cref=\"global::GFramework.Core.Rule.ContextAwareBase\" /> 的路径相比,生成实现会使用 <c>_contextSync</c> 协调惰性初始化、provider 切换和显式上下文注入;");
sb.AppendLine(
"/// <see cref=\"global::GFramework.Core.Rule.ContextAwareBase\" /> 则保持无锁的实例级缓存语义,更适合已经由调用方线程模型保证串行访问的简单场景。");
sb.AppendLine("/// </remarks>");
sb.AppendLine($"partial class {symbol.Name} : {interfaceName}");
sb.AppendLine("{");

Expand Down Expand Up @@ -128,41 +143,82 @@ private static void GenerateContextProperty(StringBuilder sb)
sb.AppendLine(" private global::GFramework.Core.Abstractions.Architectures.IArchitectureContext? _context;");
sb.AppendLine(
" private static global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider? _contextProvider;");
sb.AppendLine(" private static readonly object _contextSync = new();");
sb.AppendLine();
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider)");
sb.AppendLine(" /// 获取当前实例绑定的架构上下文。");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" /// <remarks>");
sb.AppendLine(
" /// 该属性会先返回通过 <c>IContextAware.SetContext(...)</c> 显式注入的实例上下文;若尚未设置,则在同一个同步域内惰性初始化共享提供者。");
sb.AppendLine(
" /// 当静态提供者尚未配置时,生成代码会回退到 <see cref=\"GFramework.Core.Architectures.GameContextProvider\" />。");
sb.AppendLine(
" /// 一旦某个实例成功缓存上下文,后续 <see cref=\"SetContextProvider(GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider)\" />");
sb.AppendLine(
" /// 或 <see cref=\"ResetContextProvider\" /> 不会自动清除此缓存;如需覆盖,请显式调用 <c>IContextAware.SetContext(...)</c>。");
sb.AppendLine(
" /// 当前实现还假设 <see cref=\"GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider.GetContext\" /> 可在持有 <c>_contextSync</c> 时安全执行;");
sb.AppendLine(
" /// 自定义 provider 不应在该调用链内重新进入当前类型的 provider 配置 API,且应避免引入与外部全局锁相互等待的锁顺序。");
sb.AppendLine(" /// </remarks>");
sb.AppendLine(" protected global::GFramework.Core.Abstractions.Architectures.IArchitectureContext Context");
sb.AppendLine(" {");
sb.AppendLine(" get");
sb.AppendLine(" {");
sb.AppendLine(" if (_context == null)");
sb.AppendLine(" var context = _context;");
sb.AppendLine(" if (context is not null)");
sb.AppendLine(" {");
sb.AppendLine(" return context;");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" // 在同一个同步域内协调懒加载与 provider 切换,避免读取到被并发重置的空提供者。");
sb.AppendLine(
" // provider 的 GetContext() 会在持有 _contextSync 时执行;自定义 provider 必须避免在该调用链内回调 SetContextProvider/ResetContextProvider 或形成反向锁顺序。");
sb.AppendLine(" lock (_contextSync)");
sb.AppendLine(" {");
sb.AppendLine(
" _contextProvider ??= new global::GFramework.Core.Architectures.GameContextProvider();");
sb.AppendLine(" _context = _contextProvider.GetContext();");
sb.AppendLine(" _context ??= _contextProvider.GetContext();");
sb.AppendLine(" return _context;");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" return _context;");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 配置上下文提供者(用于测试或多架构场景)");
sb.AppendLine(" /// 配置当前生成类型共享的上下文提供者。");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" /// <param name=\"provider\">上下文提供者实例</param>");
sb.AppendLine(" /// <param name=\"provider\">后续懒加载上下文时要使用的提供者实例。</param>");
sb.AppendLine(" /// <remarks>");
sb.AppendLine(" /// 该方法使用与 <see cref=\"Context\" /> 相同的同步锁,避免提供者切换与惰性初始化交错。");
sb.AppendLine(
" /// 已经缓存上下文的实例不会因为提供者切换而自动失效;该变更仅影响尚未初始化上下文的新实例或未缓存实例。");
sb.AppendLine(" /// 如需覆盖已有实例的上下文,请显式调用 <c>IContextAware.SetContext(...)</c>。");
sb.AppendLine(" /// </remarks>");
sb.AppendLine(
" public static void SetContextProvider(global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider provider)");
sb.AppendLine(" {");
sb.AppendLine(" _contextProvider = provider;");
sb.AppendLine(" lock (_contextSync)");
sb.AppendLine(" {");
sb.AppendLine(" _contextProvider = provider;");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 重置上下文提供者为默认值(用于测试清理)");
sb.AppendLine(" /// 重置共享上下文提供者,使后续懒加载回退到默认提供者。");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" /// <remarks>");
sb.AppendLine(" /// 该方法主要用于测试清理或跨用例恢复默认行为。");
sb.AppendLine(
" /// 它不会清除已经缓存到实例字段中的上下文;只有后续尚未初始化上下文的实例会重新回退到 <see cref=\"GFramework.Core.Architectures.GameContextProvider\" />。");
sb.AppendLine(" /// 如需覆盖已有实例的上下文,请显式调用 <c>IContextAware.SetContext(...)</c>。");
sb.AppendLine(" /// </remarks>");
sb.AppendLine(" public static void ResetContextProvider()");
sb.AppendLine(" {");
sb.AppendLine(" _contextProvider = null;");
sb.AppendLine(" lock (_contextSync)");
sb.AppendLine(" {");
sb.AppendLine(" _contextProvider = null;");
sb.AppendLine(" }");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
sb.AppendLine(" }");
sb.AppendLine();
}
Expand Down Expand Up @@ -232,7 +288,11 @@ private static void GenerateMethodBody(
switch (method.Name)
{
case "SetContext":
sb.AppendLine(" _context = context;");
sb.AppendLine(" // 与 Context getter 共享同一同步协议,避免显式注入被并发懒加载覆盖。");
sb.AppendLine(" lock (_contextSync)");
sb.AppendLine(" {");
sb.AppendLine(" _context = context;");
sb.AppendLine(" }");
break;

case "GetContext":
Expand Down
34 changes: 26 additions & 8 deletions GFramework.Core/Rule/ContextAwareBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,57 @@
namespace GFramework.Core.Rule;

/// <summary>
/// 上下文感知基类,实现了IContextAware接口,为需要感知架构上下文的类提供基础实现
/// 上下文感知基类,实现了 <see cref="IContextAware" />,为需要感知架构上下文的类提供基础实现
/// </summary>
/// <remarks>
/// 该基类面向手动继承场景,使用简单的实例字段缓存上下文,不提供额外同步保护。
/// 与 <c>ContextAwareGenerator</c> 生成的实现不同,它不会维护静态共享的
/// <see cref="IArchitectureContextProvider" />,也不会在 <see cref="IContextAware.SetContext" /> /
/// <see cref="IContextAware.GetContext" /> 上加锁。
/// 若调用方需要跨实例共享 provider、在惰性初始化期间协调 provider 切换,或希望生成代码自动补齐这些约束,应优先使用
/// <c>[ContextAware]</c> 生成路径;若场景本身由框架主线程驱动,且只需要最小化的实例级上下文缓存,则该基类更直接。
/// </remarks>
public abstract class ContextAwareBase : IContextAware
{
/// <summary>
/// 获取当前实例的架构上下文
/// 获取或设置当前实例缓存的架构上下文。
/// </summary>
/// <remarks>
/// 该属性不执行同步;调用方应保证对同一实例的访问遵循其自身线程模型。
/// </remarks>
protected IArchitectureContext? Context { get; set; }

/// <summary>
/// 设置架构上下文的实现方法,由框架调用
/// 设置架构上下文的实现方法,由框架调用
/// </summary>
/// <param name="context">要设置的架构上下文实例</param>
/// <param name="context">要设置的架构上下文实例。</param>
/// <remarks>
/// 该实现只做简单赋值,然后调用 <see cref="OnContextReady" />;不与 <see cref="IContextAware.GetContext" /> 共享锁。
/// </remarks>
void IContextAware.SetContext(IArchitectureContext context)
{
Context = context;
OnContextReady();
}

/// <summary>
/// 获取架构上下文
/// 获取架构上下文
/// </summary>
/// <returns>当前架构上下文对象</returns>
/// <returns>当前架构上下文对象。</returns>
/// <remarks>
/// 当 <see cref="Context" /> 为空时,该实现会直接回退到 <see cref="GameContext.GetFirstArchitectureContext" />。
/// 该回退过程不执行额外同步,也不支持替换 provider;如需这些能力,请改用生成的 ContextAware 实现。
/// </remarks>
IArchitectureContext IContextAware.GetContext()
{
Context ??= GameContext.GetFirstArchitectureContext();
return Context;
}

/// <summary>
/// 当上下文准备就绪时调用的虚方法,子类可以重写此方法来执行上下文相关的初始化逻辑
/// 当上下文准备就绪时调用的虚方法,子类可以重写此方法来执行上下文相关的初始化逻辑
/// </summary>
protected virtual void OnContextReady()
{
}
}
}
2 changes: 2 additions & 0 deletions GFramework.Cqrs.SourceGenerators/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

### New Rules

Rule ID | Category | Severity | Notes
-------------|----------------------------------|----------|------------------------------
GF_Cqrs_001 | GFramework.Cqrs.SourceGenerators | Error | CqrsHandlerRegistryGenerator
Loading
Loading