GFramework/docs/zh-CN/source-generators/priority-generator.md
GeWuYou 010ab70b7f docs(generators): 添加 Priority 和 Context Get 生成器文档
- 新增 Priority 生成器文档,介绍自动实现 IPrioritized 接口的功能
- 新增 Context Get 注入生成器文档,说明自动注入架构组件的特性
- 更新索引页面,添加新生成器的导航链接和功能描述
- 补充 EnumGenerator 配置选项说明,列出已实现和未实现的选项
- 添加完整的诊断信息说明,涵盖新生成器的所有错误场景
2026-03-29 19:54:26 +08:00

16 KiB
Raw Blame History

Priority 生成器

自动实现 IPrioritized 接口,为类添加优先级标记

Priority 生成器通过源代码生成器自动实现 IPrioritized 接口,简化优先级标记和排序逻辑的实现。

概述

核心功能

  • 自动实现接口:自动实现 IPrioritized 接口的 Priority 属性
  • 优先级标记:通过特性参数指定优先级值
  • 编译时生成:在编译时生成代码,零运行时开销
  • 类型安全:编译时类型检查,避免运行时错误

适用场景

  • 系统初始化顺序控制
  • 事件处理器优先级排序
  • 服务注册顺序管理
  • 需要按优先级排序的任何场景

基础使用

标记优先级

使用 [Priority] 特性为类标记优先级:

using GFramework.SourceGenerators.Abstractions.Bases;

[Priority(10)]
public partial class MySystem
{
    // 自动生成 IPrioritized 接口实现
}

生成代码

编译器会自动生成如下代码:

// <auto-generated/>
#nullable enable

namespace YourNamespace;

partial class MySystem : global::GFramework.Core.Abstractions.Bases.IPrioritized
{
    /// <summary>
    /// 获取优先级值: 10
    /// </summary>
    public int Priority => 10;
}

使用生成的优先级

生成的 Priority 属性可用于排序:

using GFramework.Core.Abstractions.Bases;

// 获取所有实现了 IPrioritized 的系统
var systems = new List<IPrioritized> { system1, system2, system3 };

// 按优先级排序(值越小,优先级越高)
var sorted = systems.OrderBy(s => s.Priority).ToList();

// 依次初始化
foreach (var system in sorted)
{
    system.Initialize();
}

优先级值语义

值的含义

优先级值范围 含义 使用场景
负数 高优先级 核心系统、关键事件处理器
0 默认优先级 普通系统、一般事件处理器
正数 低优先级 可延迟初始化的系统、非关键处理器

PriorityGroup 常量

推荐使用 PriorityGroup 预定义常量来标记优先级:

using GFramework.Core.Abstractions.Bases;
using GFramework.SourceGenerators.Abstractions.Bases;

[Priority(PriorityGroup.Critical)]  // -100
public partial class InputSystem : AbstractSystem { }

[Priority(PriorityGroup.High)]      // -50
public partial class PhysicsSystem : AbstractSystem { }

[Priority(PriorityGroup.Normal)]    // 0
public partial class GameplaySystem : AbstractSystem { }

[Priority(PriorityGroup.Low)]       // 50
public partial class AudioSystem : AbstractSystem { }

[Priority(PriorityGroup.Deferred)]  // 100
public partial class CleanupSystem : AbstractSystem { }

PriorityGroup 常量定义

namespace GFramework.Core.Abstractions.Bases;

public static class PriorityGroup
{
    public const int Critical = -100;   // 关键:输入、网络等
    public const int High = -50;        // 高:物理、碰撞等
    public const int Normal = 0;        // 正常:游戏逻辑等
    public const int Low = 50;          // 低:音频、特效等
    public const int Deferred = 100;    // 延迟:清理、统计等
}

使用场景

系统初始化顺序

控制系统初始化的顺序,确保依赖关系正确:

using GFramework.Core.Abstractions.Systems;
using GFramework.SourceGenerators.Abstractions.Bases;
using GFramework.Core.Abstractions.Bases;

// 输入系统最先初始化
[Priority(PriorityGroup.Critical)]
public partial class InputSystem : AbstractSystem
{
    protected override void OnInit()
    {
        // 初始化输入处理
    }
}

// 物理系统次之
[Priority(PriorityGroup.High)]
public partial class PhysicsSystem : AbstractSystem
{
    protected override void OnInit()
    {
        // 初始化物理引擎
    }
}

// 游戏逻辑系统在中间
[Priority(PriorityGroup.Normal)]
public partial class GameplaySystem : AbstractSystem
{
    protected override void OnInit()
    {
        // 初始化游戏逻辑
    }
}

// 音频系统可以稍后
[Priority(PriorityGroup.Low)]
public partial class AudioSystem : AbstractSystem
{
    protected override void OnInit()
    {
        // 初始化音频引擎
    }
}

在架构中按优先级初始化:

public class GameArchitecture : Architecture
{
    protected override void InitSystems()
    {
        // 获取所有系统并按优先级排序
        var systems = this.GetAllByPriority<ISystem>();

        foreach (var system in systems)
        {
            system.Init();
        }
    }
}

事件处理器优先级

控制事件处理器的执行顺序:

using GFramework.Core.Abstractions.Events;

// 关键事件处理器最先执行
[Priority(PriorityGroup.Critical)]
public partial class CriticalEventHandler : IEventHandler<CriticalEvent>
{
    public void Handle(CriticalEvent e)
    {
        // 处理关键事件
    }
}

// 普通处理器在中间执行
[Priority(PriorityGroup.Normal)]
public partial class NormalEventHandler : IEventHandler<CriticalEvent>
{
    public void Handle(CriticalEvent e)
    {
        // 处理普通逻辑
    }
}

// 日志记录器最后执行
[Priority(PriorityGroup.Deferred)]
public partial class EventLogger : IEventHandler<CriticalEvent>
{
    public void Handle(CriticalEvent e)
    {
        // 记录日志
    }
}

事件总线按优先级调用处理器:

public class EventBus : IEventBus
{
    public void Send<TEvent>(TEvent e) where TEvent : IEvent
    {
        // 获取所有处理器并按优先级排序
        var handlers = this.GetAllByPriority<IEventHandler<TEvent>>();

        foreach (var handler in handlers)
        {
            handler.Handle(e);
        }
    }
}

服务排序

控制多个服务实现的优先级:

// 高优先级服务
[Priority(PriorityGroup.High)]
public partial class PremiumService : IService
{
    public void Execute()
    {
        // 优先执行
    }
}

// 默认服务
[Priority(PriorityGroup.Normal)]
public partial class DefaultService : IService
{
    public void Execute()
    {
        // 默认执行
    }
}

// 后备服务
[Priority(PriorityGroup.Low)]
public partial class FallbackService : IService
{
    public void Execute()
    {
        // 最后备选
    }
}

与 PriorityUsageAnalyzer 集成

GF_Priority_Usage_001 诊断

PriorityUsageAnalyzer 分析器会检测应该使用 GetAllByPriority<T>() 而非 GetAll<T>() 的场景:

错误示例

// ❌ 不推荐:可能未按优先级排序
var systems = context.GetAll<ISystem>();

正确示例

// ✅ 推荐:确保按优先级排序
var systems = context.GetAllByPriority<ISystem>();

分析器规则

当满足以下条件时,分析器会报告 GF_Priority_Usage_001 诊断:

  1. 类型实现了 IPrioritized 接口
  2. 使用了 GetAll<T>() 方法
  3. 建议改用 GetAllByPriority<T>() 方法

诊断信息

GF_Priority_001 - 只能应用于类

错误信息Priority attribute can only be applied to classes

场景:将 [Priority] 特性应用于非类类型

[Priority(10)]
public interface IMyInterface  // ❌ 错误
{
}

[Priority(10)]
public struct MyStruct  // ❌ 错误
{
}

解决方案:只在类上使用 [Priority] 特性

[Priority(10)]
public partial class MyClass  // ✅ 正确
{
}

GF_Priority_002 - 已实现 IPrioritized 接口

错误信息Type '{ClassName}' already implements IPrioritized interface

场景:类已手动实现 IPrioritized 接口

[Priority(10)]
public partial class MySystem : IPrioritized  // ❌ 冲突
{
    public int Priority => 10;  // 手动实现
}

解决方案:移除 [Priority] 特性或移除手动实现

// 方案1移除特性使用手动实现
public partial class MySystem : IPrioritized
{
    public int Priority => 10;
}

// 方案2移除手动实现使用生成器
[Priority(10)]
public partial class MySystem
{
    // 生成器自动实现
}

GF_Priority_003 - 必须声明为 partial

错误信息Class '{ClassName}' must be declared as partial

场景:类未声明为 partial

[Priority(10)]
public class MySystem  // ❌ 缺少 partial
{
}

解决方案:添加 partial 关键字

[Priority(10)]
public partial class MySystem  // ✅ 正确
{
}

GF_Priority_004 - 优先级值无效

错误信息Priority value is invalid

场景:特性参数无效或未提供

[Priority]  // ❌ 缺少参数
public partial class MySystem
{
}

解决方案:提供有效的优先级值

[Priority(10)]  // ✅ 正确
public partial class MySystem
{
}

GF_Priority_005 - 不支持嵌套类

错误信息Nested class '{ClassName}' is not supported

场景:在嵌套类中使用 [Priority] 特性

public partial class OuterClass
{
    [Priority(10)]
    public partial class InnerClass  // ❌ 错误
    {
    }
}

解决方案:将嵌套类提取为独立的类

[Priority(10)]
public partial class InnerClass  // ✅ 正确
{
}

最佳实践

1. 使用 PriorityGroup 常量

避免使用魔法数字,优先使用预定义常量:

// ✅ 推荐:使用常量
[Priority(PriorityGroup.Critical)]
public partial class InputSystem { }

// ❌ 不推荐:魔法数字
[Priority(-100)]
public partial class InputSystem { }

2. 预留优先级间隔

为未来扩展预留间隔:

public static class SystemPriority
{
    public const int Input = -100;
    public const int PrePhysics = -90;   // 预留扩展
    public const int Physics = -80;
    public const int PostPhysics = -70;  // 预留扩展
    public const int Gameplay = 0;
    public const int PostGameplay = 10;  // 预留扩展
    public const int Audio = 50;
    public const int Cleanup = 100;
}

3. 文档化优先级语义

为自定义优先级值添加注释:

/// <summary>
/// 输入系统,优先级 -100需要最先初始化以接收输入事件
/// </summary>
[Priority(PriorityGroup.Critical)]
public partial class InputSystem : AbstractSystem
{
}

4. 避免优先级冲突

当多个类有相同优先级时,执行顺序不确定。应避免依赖特定顺序:

// ❌ 不推荐:相同优先级,顺序不确定
[Priority(0)]
public partial class SystemA { }

[Priority(0)]
public partial class SystemB { }

// ✅ 推荐:明确区分优先级
[Priority(-10)]
public partial class SystemA { }

[Priority(0)]
public partial class SystemB { }

5. 只在真正需要排序的场景使用

不要滥用优先级特性,只在确实需要排序的场景使用:

// ✅ 推荐:需要排序的系统
[Priority(PriorityGroup.Critical)]
public partial class InputSystem : AbstractSystem { }

// ❌ 不推荐:不需要排序的工具类
[Priority(10)]
public static partial class MathHelper  // 静态工具类无需优先级
{
    public static int Add(int a, int b) => a + b;
}

高级场景

泛型类支持

Priority 特性支持泛型类:

[Priority(20)]
public partial class GenericSystem<T> : ISystem
{
    public void Init()
    {
        // 泛型系统的初始化
    }
}

与其他特性组合

Priority 可以与其他源代码生成器特性组合使用:

using GFramework.SourceGenerators.Abstractions.Bases;
using GFramework.SourceGenerators.Abstractions.Logging;
using GFramework.SourceGenerators.Abstractions.Rule;

[Priority(PriorityGroup.High)]
[Log]
[ContextAware]
public partial class HighPrioritySystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("High priority system initialized");
    }
}

运行时优先级查询

可以在运行时查询优先级值:

public void ProcessSystems()
{
    var systems = this.GetAllByPriority<ISystem>();

    foreach (var system in systems)
    {
        if (system is IPrioritized prioritized)
        {
            Console.WriteLine($"System: {system.GetType().Name}, Priority: {prioritized.Priority}");
        }

        system.Init();
    }
}

常见问题

Q: 优先级值可以是任意整数吗?

A: 是的,优先级值可以是任何 int 类型的值。推荐使用 PriorityGroup 预定义常量或在项目中定义自己的优先级常量。

Q: 多个类有相同优先级会怎样?

A: 当多个类有相同的优先级值时,它们的相对顺序是不确定的。建议为每个类设置不同的优先级值,或在文档中明确说明相同优先级类的执行顺序不保证。

Q: 可以在运行时改变优先级吗?

A: 不可以。Priority 属性是只读的,值在编译时确定。如果需要运行时改变优先级,应手动实现 IPrioritized 接口。

public class DynamicPrioritySystem : IPrioritized
{
    private int _priority;

    public int Priority => _priority;

    public void SetPriority(int value)
    {
        _priority = value;
    }
}

Q: 优先级支持继承吗?

A: [Priority] 特性标记为 Inherited = false,不会被子类继承。每个子类需要独立标记优先级。

[Priority(10)]
public partial class BaseSystem { }

public partial class DerivedSystem : BaseSystem  // 不会继承 Priority = 10
{
    // 需要重新标记
    // [Priority(20)]
}

Q: 如何在不使用特性的情况下实现优先级?

A: 可以手动实现 IPrioritized 接口:

public class ManualPrioritySystem : IPrioritized
{
    public int Priority => 10;

    // 手动实现提供更大的灵活性
    public int Priority => _config.Enabled ? 10 : 100;
}

Q: 负数优先级和正数优先级的区别是什么?

A: 优先级值用于排序,通常值越小优先级越高:

  • 负数(-100高优先级最先执行
  • 0默认优先级
  • 正数100低优先级最后执行

具体含义取决于使用场景,但推荐遵循 PriorityGroup 定义的语义。

实际应用示例

游戏系统架构

完整的游戏系统初始化示例:

using GFramework.Core.Abstractions.Systems;
using GFramework.SourceGenerators.Abstractions.Bases;
using GFramework.Core.Abstractions.Bases;

// 输入系统(最先初始化)
[Priority(PriorityGroup.Critical)]
[Log]
public partial class InputSystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("Input system initialized");
    }
}

// 物理系统
[Priority(PriorityGroup.High)]
[Log]
public partial class PhysicsSystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("Physics system initialized");
    }
}

// 游戏逻辑系统
[Priority(PriorityGroup.Normal)]
[Log]
[ContextAware]
public partial class GameplaySystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("Gameplay system initialized");
    }
}

// 音频系统
[Priority(PriorityGroup.Low)]
[Log]
public partial class AudioSystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("Audio system initialized");
    }
}

// 清理系统(最后执行)
[Priority(PriorityGroup.Deferred)]
[Log]
public partial class CleanupSystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("Cleanup system initialized");
    }
}

在架构中初始化:

public class GameArchitecture : Architecture
{
    protected override async Task InitAsync()
    {
        // 获取所有系统并按优先级排序
        var systems = this.GetAllByPriority<ISystem>();

        foreach (var system in systems)
        {
            await system.InitAsync();
        }
    }
}

相关文档