feat(source-generators): 添加多个代码生成器功能

- 新增 PriorityGenerator 为标记 Priority 特性的类自动生成 IPrioritized 接口实现
- 新增 EnumExtensionsGenerator 为枚举自动生成 Is 和 IsIn 扩展方法
- 新增 LoggerGenerator 为标记 Log 特性的类自动生成日志字段
- 新增 ContextAwareGenerator 为标记 ContextAware 特性的类自动生成 IContextAware 接口实现
- 新增 CqrsHandlerRegistryGenerator 为 CQRS 处理器生成编译时注册器减少运行时反射开销
This commit is contained in:
GeWuYou 2026-04-17 10:07:57 +08:00
parent 35a1634697
commit b19877f970
26 changed files with 299 additions and 50 deletions

View File

@ -118,6 +118,9 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
? $"<{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("{");

View File

@ -95,6 +95,9 @@ public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase
sb.AppendLine("{");
sb.AppendLine(" /// <summary>");
sb.AppendLine($" /// 为 <see cref=\"{fullEnumName}\" /> 提供自动生成的扩展方法。");
sb.AppendLine(" /// </summary>");
sb.AppendLine($" public static partial class {enumName}Extensions");
sb.AppendLine(" {");
@ -176,7 +179,13 @@ public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase
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;
@ -192,7 +201,13 @@ public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase
/// <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\">用于匹配的候选枚举值集合。</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(" {");

View File

@ -71,13 +71,18 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase
.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}\");")

View File

@ -96,6 +96,9 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
var interfaceName = iContextAware.ToDisplayString(
SymbolDisplayFormat.FullyQualifiedFormat);
sb.AppendLine("/// <summary>");
sb.AppendLine("/// 为当前规则类型补充自动生成的架构上下文访问实现。");
sb.AppendLine("/// </summary>");
sb.AppendLine($"partial class {symbol.Name} : {interfaceName}");
sb.AppendLine("{");
@ -128,6 +131,7 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
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");
@ -136,14 +140,20 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
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(" 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;");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" return _context;");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine();
@ -154,7 +164,10 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
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>");
@ -162,7 +175,10 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
sb.AppendLine(" /// </summary>");
sb.AppendLine(" public static void ResetContextProvider()");
sb.AppendLine(" {");
sb.AppendLine(" _contextProvider = null;");
sb.AppendLine(" lock (_contextSync)");
sb.AppendLine(" {");
sb.AppendLine(" _contextProvider = null;");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine();
}

View File

@ -165,8 +165,9 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
.Cast<string>()
.ToArray();
if (fallbackHandlerTypeMetadataNames.Length > 0 &&
!generationEnvironment.SupportsReflectionFallbackAttribute)
if (!CanEmitGeneratedRegistry(
generationEnvironment.SupportsReflectionFallbackAttribute,
fallbackHandlerTypeMetadataNames.Length))
{
return;
}
@ -176,6 +177,26 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
GenerateSource(generationEnvironment, registrations, fallbackHandlerTypeMetadataNames));
}
/// <summary>
/// 判断当前轮次是否允许输出生成注册器。
/// </summary>
/// <param name="supportsReflectionFallbackAttribute">
/// runtime 合同中是否存在 <c>CqrsReflectionFallbackAttribute</c>,以承载生成器无法静态精确表达的 handler 回退元数据。
/// </param>
/// <param name="fallbackHandlerTypeCount">
/// 当前轮次需要依赖程序集级 reflection fallback 元数据恢复的 handler 数量。
/// </param>
/// <returns>
/// 当没有 handler 依赖 fallback或 runtime 已提供承载该元数据的特性契约时返回 <see langword="true" />
/// 否则返回 <see langword="false" />,调用方必须放弃生成以避免输出会静默漏注册的半成品注册器。
/// </returns>
private static bool CanEmitGeneratedRegistry(
bool supportsReflectionFallbackAttribute,
int fallbackHandlerTypeCount)
{
return fallbackHandlerTypeCount == 0 || supportsReflectionFallbackAttribute;
}
private static List<ImplementationRegistrationSpec> CollectRegistrations(
ImmutableArray<HandlerCandidateAnalysis?> candidates)
{

View File

@ -3,10 +3,13 @@
namespace TestApp;
/// <summary>
/// 为当前分部类型补充自动生成的优先级契约实现。
/// </summary>
partial class MySystem : global::GFramework.Core.Abstractions.Bases.IPrioritized
{
/// <summary>
/// 获取优先级值: 10
/// </summary>
public int Priority => 10;
}
}

View File

@ -3,10 +3,13 @@
namespace TestApp;
/// <summary>
/// 为当前分部类型补充自动生成的优先级契约实现。
/// </summary>
partial class GenericSystem<T> : global::GFramework.Core.Abstractions.Bases.IPrioritized
{
/// <summary>
/// 获取优先级值: 20
/// </summary>
public int Priority => 20;
}
}

View File

@ -3,10 +3,13 @@
namespace TestApp;
/// <summary>
/// 为当前分部类型补充自动生成的优先级契约实现。
/// </summary>
partial class CriticalSystem : global::GFramework.Core.Abstractions.Bases.IPrioritized
{
/// <summary>
/// 获取优先级值: -100
/// </summary>
public int Priority => -100;
}
}

View File

@ -3,10 +3,13 @@
namespace TestApp;
/// <summary>
/// 为当前分部类型补充自动生成的优先级契约实现。
/// </summary>
partial class HighPrioritySystem : global::GFramework.Core.Abstractions.Bases.IPrioritized
{
/// <summary>
/// 获取优先级值: -50
/// </summary>
public int Priority => -50;
}
}

View File

@ -42,7 +42,7 @@ public static class GeneratorSnapshotTest<TGenerator>
compilationErrors,
Is.Empty,
() =>
$"编译生成的代码时出现错误:{Environment.NewLine}{string.Join(Environment.NewLine, compilationErrors.Select(static diagnostic => diagnostic.ToString()))}");
$"编译生成的代码时出现错误{Environment.NewLine}{string.Join(Environment.NewLine, compilationErrors.Select(static diagnostic => diagnostic.ToString()))}");
var runResult = driver.GetRunResult();
var generated = runResult.Results
@ -53,7 +53,7 @@ public static class GeneratorSnapshotTest<TGenerator>
Assert.That(
generated,
Is.Not.Empty,
$"Generator '{typeof(TGenerator).FullName}' did not produce any sources.");
$"生成器 '{typeof(TGenerator).FullName}' 未产生任何输出。");
foreach (var (filename, content) in generated)
{
@ -70,7 +70,7 @@ public static class GeneratorSnapshotTest<TGenerator>
await File.WriteAllTextAsync(path, content.ToString());
Assert.Fail(
$"Snapshot not found. Generated new snapshot at:\n{path}");
$"未找到快照文件,已在以下路径生成新快照:\n{path}");
}
var expected = await File.ReadAllTextAsync(path);
@ -78,7 +78,7 @@ public static class GeneratorSnapshotTest<TGenerator>
Assert.That(
Normalize(expected),
Is.EqualTo(Normalize(content.ToString())),
$"Snapshot mismatch: {snapshotFileName}");
$"快照不匹配:{snapshotFileName}");
}
}

View File

@ -1168,6 +1168,32 @@ public class CqrsHandlerRegistryGeneratorTests
("CqrsHandlerRegistry.g.cs", HiddenNestedHandlerSelfRegistrationExpected));
}
/// <summary>
/// 验证当某轮生成仍然需要程序集级 reflection fallback 元数据时,
/// 若 runtime 合同未提供对应特性契约,生成器会放弃输出注册器以避免静默漏注册。
/// </summary>
[Test]
public void
Rejects_Registry_Emission_When_Fallback_Metadata_Is_Required_But_Runtime_Contract_Lacks_Fallback_Attribute()
{
var method = typeof(CqrsHandlerRegistryGenerator).GetMethod(
"CanEmitGeneratedRegistry",
BindingFlags.NonPublic | BindingFlags.Static);
Assert.That(method, Is.Not.Null);
var canEmitWithoutFallbackRequirement = (bool?)method!.Invoke(null, [false, 0]);
var canEmitWithSupportedFallbackAttribute = (bool?)method.Invoke(null, [true, 1]);
var canEmitWithoutSupportedFallbackAttribute = (bool?)method.Invoke(null, [false, 1]);
Assert.Multiple(() =>
{
Assert.That(canEmitWithoutFallbackRequirement, Is.True);
Assert.That(canEmitWithSupportedFallbackAttribute, Is.True);
Assert.That(canEmitWithoutSupportedFallbackAttribute, Is.False);
});
}
/// <summary>
/// 验证日志字符串转义会覆盖换行、反斜杠和双引号,避免生成代码中的字符串字面量被意外截断。
/// </summary>

View File

@ -15,6 +15,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证默认配置会为普通枚举生成逐项判断方法与集合判断方法。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_BasicEnum_IsMethods()
{
@ -37,6 +38,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证未提供快照文件名映射时,会直接按生成文件名进行快照比对。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_BasicEnum_IsMethods_DefaultSnapshotFileNameSelector()
{
@ -57,6 +59,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证默认配置在较小枚举上仍会生成集合判断方法。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_BasicEnum_IsInMethod()
{
@ -78,6 +81,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证带显式位标志值的枚举也会生成对应扩展方法。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_EnumWithFlagValues()
{
@ -102,6 +106,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证关闭逐项判断开关后仅保留集合判断方法。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_DisableIsMethods()
{
@ -124,6 +129,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证关闭集合判断开关后仅保留逐项判断方法。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_DisableIsInMethod()
{
@ -146,6 +152,7 @@ public class EnumExtensionsGeneratorSnapshotTests
/// <summary>
/// 验证同时关闭两个生成开关时不会输出任何扩展方法。
/// </summary>
/// <returns>异步任务。</returns>
[Test]
public async Task Snapshot_DisableAllGeneratedMethods()
{

View File

@ -2,15 +2,31 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Status" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class StatusExtensions
{
/// <summary>是否为 Active</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Active" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Active" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
/// <summary>是否为 Inactive</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Inactive" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Inactive" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
/// <summary>判断是否属于指定集合</summary>
/// <summary>
/// 判断给定值是否属于指定候选集合。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <param name="values">用于匹配的候选枚举值集合。</param>
/// <returns>当 <paramref name="value" /> 命中任一候选值时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
{
if (values == null) return false;

View File

@ -2,18 +2,38 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Status" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class StatusExtensions
{
/// <summary>是否为 Active</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Active" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Active" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
/// <summary>是否为 Inactive</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Inactive" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Inactive" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
/// <summary>是否为 Pending</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Pending" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Pending" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsPending(this TestApp.Status value) => value == TestApp.Status.Pending;
/// <summary>判断是否属于指定集合</summary>
/// <summary>
/// 判断给定值是否属于指定候选集合。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <param name="values">用于匹配的候选枚举值集合。</param>
/// <returns>当 <paramref name="value" /> 命中任一候选值时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
{
if (values == null) return false;

View File

@ -2,15 +2,31 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Status" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class StatusExtensions
{
/// <summary>是否为 Active</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Active" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Active" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
/// <summary>是否为 Inactive</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Inactive" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Inactive" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
/// <summary>判断是否属于指定集合</summary>
/// <summary>
/// 判断给定值是否属于指定候选集合。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <param name="values">用于匹配的候选枚举值集合。</param>
/// <returns>当 <paramref name="value" /> 命中任一候选值时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
{
if (values == null) return false;

View File

@ -2,6 +2,9 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Status" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class StatusExtensions
{
}

View File

@ -2,12 +2,23 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Status" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class StatusExtensions
{
/// <summary>是否为 Active</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Active" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Active" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
/// <summary>是否为 Inactive</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Status.Inactive" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Status.Inactive" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
}
}

View File

@ -2,9 +2,17 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Status" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class StatusExtensions
{
/// <summary>判断是否属于指定集合</summary>
/// <summary>
/// 判断给定值是否属于指定候选集合。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <param name="values">用于匹配的候选枚举值集合。</param>
/// <returns>当 <paramref name="value" /> 命中任一候选值时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
{
if (values == null) return false;

View File

@ -2,21 +2,45 @@
using System;
namespace TestApp
{
/// <summary>
/// 为 <see cref="TestApp.Permissions" /> 提供自动生成的扩展方法。
/// </summary>
public static partial class PermissionsExtensions
{
/// <summary>是否为 None</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Permissions.None" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Permissions.None" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsNone(this TestApp.Permissions value) => value == TestApp.Permissions.None;
/// <summary>是否为 Read</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Permissions.Read" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Permissions.Read" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsRead(this TestApp.Permissions value) => value == TestApp.Permissions.Read;
/// <summary>是否为 Write</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Permissions.Write" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Permissions.Write" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsWrite(this TestApp.Permissions value) => value == TestApp.Permissions.Write;
/// <summary>是否为 Execute</summary>
/// <summary>
/// 判断给定值是否为 <see cref="TestApp.Permissions.Execute" />。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <returns>当 <paramref name="value" /> 等于 <see cref="TestApp.Permissions.Execute" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsExecute(this TestApp.Permissions value) => value == TestApp.Permissions.Execute;
/// <summary>判断是否属于指定集合</summary>
/// <summary>
/// 判断给定值是否属于指定候选集合。
/// </summary>
/// <param name="value">要检查的枚举值。</param>
/// <param name="values">用于匹配的候选枚举值集合。</param>
/// <returns>当 <paramref name="value" /> 命中任一候选值时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public static bool IsIn(this TestApp.Permissions value, params TestApp.Permissions[] values)
{
if (values == null) return false;

View File

@ -4,8 +4,13 @@ using GFramework.Core.Logging;
namespace TestApp;
/// <summary>
/// 为当前分部类型提供自动生成的日志字段。
/// </summary>
partial class MyService
{
/// <summary>Auto-generated logger</summary>
/// <summary>
/// 自动生成的日志字段。
/// </summary>
private static readonly ILogger MyLogger = LoggerFactoryResolver.Provider.CreateLogger("MyService");
}

View File

@ -4,8 +4,13 @@ using GFramework.Core.Logging;
namespace TestApp;
/// <summary>
/// 为当前分部类型提供自动生成的日志字段。
/// </summary>
partial class MyService
{
/// <summary>Auto-generated logger</summary>
/// <summary>
/// 自动生成的日志字段。
/// </summary>
private static readonly ILogger _log = LoggerFactoryResolver.Provider.CreateLogger("MyService");
}

View File

@ -4,8 +4,13 @@ using GFramework.Core.Logging;
namespace TestApp;
/// <summary>
/// 为当前分部类型提供自动生成的日志字段。
/// </summary>
partial class MyService
{
/// <summary>Auto-generated logger</summary>
/// <summary>
/// 自动生成的日志字段。
/// </summary>
private static readonly ILogger _log = LoggerFactoryResolver.Provider.CreateLogger("MyService");
}

View File

@ -4,8 +4,13 @@ using GFramework.Core.Logging;
namespace TestApp;
/// <summary>
/// 为当前分部类型提供自动生成的日志字段。
/// </summary>
partial class MyService<T>
{
/// <summary>Auto-generated logger</summary>
/// <summary>
/// 自动生成的日志字段。
/// </summary>
private static readonly ILogger _log = LoggerFactoryResolver.Provider.CreateLogger("MyService");
}

View File

@ -4,8 +4,13 @@ using GFramework.Core.Logging;
namespace TestApp;
/// <summary>
/// 为当前分部类型提供自动生成的日志字段。
/// </summary>
partial class MyService
{
/// <summary>Auto-generated logger</summary>
/// <summary>
/// 自动生成的日志字段。
/// </summary>
private readonly ILogger _log = LoggerFactoryResolver.Provider.CreateLogger("MyService");
}

View File

@ -4,8 +4,13 @@ using GFramework.Core.Logging;
namespace TestApp;
/// <summary>
/// 为当前分部类型提供自动生成的日志字段。
/// </summary>
partial class MyService
{
/// <summary>Auto-generated logger</summary>
/// <summary>
/// 自动生成的日志字段。
/// </summary>
public static readonly ILogger _log = LoggerFactoryResolver.Provider.CreateLogger("MyService");
}

View File

@ -3,10 +3,14 @@
namespace TestApp;
/// <summary>
/// 为当前规则类型补充自动生成的架构上下文访问实现。
/// </summary>
partial class MyRule : global::GFramework.Core.Abstractions.Rule.IContextAware
{
private global::GFramework.Core.Abstractions.Architectures.IArchitectureContext? _context;
private static global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider? _contextProvider;
private static readonly object _contextSync = new();
/// <summary>
/// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider
@ -15,13 +19,19 @@ partial class MyRule : global::GFramework.Core.Abstractions.Rule.IContextAware
{
get
{
if (_context == null)
var context = _context;
if (context is not null)
{
_contextProvider ??= new global::GFramework.Core.Architectures.GameContextProvider();
_context = _contextProvider.GetContext();
return context;
}
return _context;
// 在同一个同步域内协调懒加载与 provider 切换,避免读取到被并发重置的空提供者。
lock (_contextSync)
{
_contextProvider ??= new global::GFramework.Core.Architectures.GameContextProvider();
_context ??= _contextProvider.GetContext();
return _context;
}
}
}
@ -31,7 +41,10 @@ partial class MyRule : global::GFramework.Core.Abstractions.Rule.IContextAware
/// <param name="provider">上下文提供者实例</param>
public static void SetContextProvider(global::GFramework.Core.Abstractions.Architectures.IArchitectureContextProvider provider)
{
_contextProvider = provider;
lock (_contextSync)
{
_contextProvider = provider;
}
}
/// <summary>
@ -39,7 +52,10 @@ partial class MyRule : global::GFramework.Core.Abstractions.Rule.IContextAware
/// </summary>
public static void ResetContextProvider()
{
_contextProvider = null;
lock (_contextSync)
{
_contextProvider = null;
}
}
void global::GFramework.Core.Abstractions.Rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.Architectures.IArchitectureContext context)
@ -52,4 +68,4 @@ partial class MyRule : global::GFramework.Core.Abstractions.Rule.IContextAware
return Context;
}
}
}