mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-30 01:06:43 +08:00
Merge pull request #149 from GeWuYou/docs/generators-priority-context-injection
docs(generators): 添加 Priority 和 Context Get 生成器文档
This commit is contained in:
commit
76a1406346
@ -1,7 +1,53 @@
|
|||||||
import { defineConfig } from 'vitepress'
|
import { defineConfig } from 'vitepress'
|
||||||
|
|
||||||
|
function safeGenericEscapePlugin() {
|
||||||
|
return {
|
||||||
|
name: 'safe-generic-escape',
|
||||||
|
enforce: 'pre',
|
||||||
|
|
||||||
|
transform(code: string, id: string) {
|
||||||
|
if (!id.endsWith('.md')) return
|
||||||
|
|
||||||
|
const codeBlocks: string[] = []
|
||||||
|
const htmlBlocks: string[] = []
|
||||||
|
|
||||||
|
// 1️⃣ 保护代码块 ``` ```
|
||||||
|
let processed = code.replace(/```[\s\S]*?```/g, (match) => {
|
||||||
|
const i = codeBlocks.length
|
||||||
|
codeBlocks.push(match)
|
||||||
|
return `__CODE_BLOCK_${i}__`
|
||||||
|
})
|
||||||
|
|
||||||
|
// 2️⃣ 保护 HTML 标签(避免破坏 Vue SFC)
|
||||||
|
processed = processed.replace(/<\/?[a-zA-Z][^>]*>/g, (match) => {
|
||||||
|
const i = htmlBlocks.length
|
||||||
|
htmlBlocks.push(match)
|
||||||
|
return `__HTML_BLOCK_${i}__`
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3️⃣ 只转义"泛型形式"的 <T> 或 <K, V>
|
||||||
|
processed = processed.replace(
|
||||||
|
/<([A-Z][A-Za-z0-9_,\s]*)>/g,
|
||||||
|
(_, inner) => `<${inner}>`
|
||||||
|
)
|
||||||
|
|
||||||
|
// 4️⃣ 恢复 HTML
|
||||||
|
htmlBlocks.forEach((block, i) => {
|
||||||
|
processed = processed.replace(`__HTML_BLOCK_${i}__`, block)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 5️⃣ 恢复代码块
|
||||||
|
codeBlocks.forEach((block, i) => {
|
||||||
|
processed = processed.replace(`__CODE_BLOCK_${i}__`, block)
|
||||||
|
})
|
||||||
|
|
||||||
|
return processed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
|
||||||
title: 'GFramework',
|
title: 'GFramework',
|
||||||
description: '面向游戏开发场景的模块化 C# 框架',
|
description: '面向游戏开发场景的模块化 C# 框架',
|
||||||
head: [
|
head: [
|
||||||
@ -10,7 +56,11 @@ export default defineConfig({
|
|||||||
/** GitHub Pages / 子路径部署 */
|
/** GitHub Pages / 子路径部署 */
|
||||||
base: '/GFramework/',
|
base: '/GFramework/',
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [safeGenericEscapePlugin()]
|
plugins: [safeGenericEscapePlugin()],
|
||||||
|
build: {
|
||||||
|
// 提高代码块大小警告阈值(文档包含大量代码示例)
|
||||||
|
chunkSizeWarningLimit: 1000
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/** 多语言 */
|
/** 多语言 */
|
||||||
locales: {
|
locales: {
|
||||||
@ -162,7 +212,9 @@ export default defineConfig({
|
|||||||
{ text: '概览', link: '/zh-CN/source-generators/' },
|
{ text: '概览', link: '/zh-CN/source-generators/' },
|
||||||
{ text: '日志生成器', link: '/zh-CN/source-generators/logging-generator' },
|
{ text: '日志生成器', link: '/zh-CN/source-generators/logging-generator' },
|
||||||
{ text: '枚举扩展', link: '/zh-CN/source-generators/enum-generator' },
|
{ text: '枚举扩展', link: '/zh-CN/source-generators/enum-generator' },
|
||||||
{ text: 'ContextAware 生成器', link: '/zh-CN/source-generators/context-aware-generator' }
|
{ text: 'ContextAware 生成器', link: '/zh-CN/source-generators/context-aware-generator' },
|
||||||
|
{ text: 'Priority 生成器', link: '/zh-CN/source-generators/priority-generator' },
|
||||||
|
{ text: 'Context Get 注入', link: '/zh-CN/source-generators/context-get-generator' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -251,50 +303,3 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
import { defineConfig } from 'vitepress'
|
|
||||||
|
|
||||||
function safeGenericEscapePlugin() {
|
|
||||||
return {
|
|
||||||
name: 'safe-generic-escape',
|
|
||||||
enforce: 'pre',
|
|
||||||
|
|
||||||
transform(code: string, id: string) {
|
|
||||||
if (!id.endsWith('.md')) return
|
|
||||||
|
|
||||||
const codeBlocks: string[] = []
|
|
||||||
const htmlBlocks: string[] = []
|
|
||||||
|
|
||||||
// 1️⃣ 保护代码块 ``` ```
|
|
||||||
let processed = code.replace(/```[\s\S]*?```/g, (match) => {
|
|
||||||
const i = codeBlocks.length
|
|
||||||
codeBlocks.push(match)
|
|
||||||
return `__CODE_BLOCK_${i}__`
|
|
||||||
})
|
|
||||||
|
|
||||||
// 2️⃣ 保护 HTML 标签(避免破坏 Vue SFC)
|
|
||||||
processed = processed.replace(/<\/?[a-zA-Z][^>]*>/g, (match) => {
|
|
||||||
const i = htmlBlocks.length
|
|
||||||
htmlBlocks.push(match)
|
|
||||||
return `__HTML_BLOCK_${i}__`
|
|
||||||
})
|
|
||||||
|
|
||||||
// 3️⃣ 只转义“泛型形式”的 <T> 或 <K, V>
|
|
||||||
processed = processed.replace(
|
|
||||||
/<([A-Z][A-Za-z0-9_,\s]*)>/g,
|
|
||||||
(_, inner) => `<${inner}>`
|
|
||||||
)
|
|
||||||
|
|
||||||
// 4️⃣ 恢复 HTML
|
|
||||||
htmlBlocks.forEach((block, i) => {
|
|
||||||
processed = processed.replace(`__HTML_BLOCK_${i}__`, block)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 5️⃣ 恢复代码块
|
|
||||||
codeBlocks.forEach((block, i) => {
|
|
||||||
processed = processed.replace(`__CODE_BLOCK_${i}__`, block)
|
|
||||||
})
|
|
||||||
|
|
||||||
return processed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
759
docs/zh-CN/source-generators/context-get-generator.md
Normal file
759
docs/zh-CN/source-generators/context-get-generator.md
Normal file
@ -0,0 +1,759 @@
|
|||||||
|
# Context Get 注入生成器
|
||||||
|
|
||||||
|
> 自动注入架构组件,消除样板代码
|
||||||
|
|
||||||
|
Context Get 注入生成器提供自动注入架构组件的能力,通过源代码生成器在编译时自动生成依赖注入代码,无需手动调用
|
||||||
|
GetModel/GetSystem 等方法。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
### 核心功能
|
||||||
|
|
||||||
|
- **自动注入**:自动注入 Model、System、Utility 和 Service 实例
|
||||||
|
- **集合注入**:支持注入多个同类型组件的集合
|
||||||
|
- **智能推断**:`[GetAll]` 特性可自动识别字段类型并注入
|
||||||
|
- **零运行时开销**:编译时生成代码,无反射或动态调用
|
||||||
|
|
||||||
|
### 与 ContextAware 的关系
|
||||||
|
|
||||||
|
Context Get 注入依赖 `[ContextAware]` 特性提供的上下文访问能力。使用注入特性前,类必须:
|
||||||
|
|
||||||
|
- 标记 `[ContextAware]` 特性,或
|
||||||
|
- 实现 `IContextAware` 接口,或
|
||||||
|
- 继承 `ContextAwareBase` 类
|
||||||
|
|
||||||
|
## 注入特性
|
||||||
|
|
||||||
|
### 支持的9个特性
|
||||||
|
|
||||||
|
| 特性 | 应用目标 | 功能描述 | 类型约束 |
|
||||||
|
|------------------|------|------------------|------------------------------------|
|
||||||
|
| `[GetModel]` | 字段 | 注入单个 Model 实例 | 必须实现 IModel |
|
||||||
|
| `[GetModels]` | 字段 | 注入 Model 集合 | IReadOnlyList\<IModel\> |
|
||||||
|
| `[GetSystem]` | 字段 | 注入单个 System 实例 | 必须实现 ISystem |
|
||||||
|
| `[GetSystems]` | 字段 | 注入 System 集合 | IReadOnlyList\<ISystem\> |
|
||||||
|
| `[GetUtility]` | 字段 | 注入单个 Utility 实例 | 必须实现 IUtility |
|
||||||
|
| `[GetUtilities]` | 字段 | 注入 Utility 集合 | IReadOnlyList\<IUtility\> |
|
||||||
|
| `[GetService]` | 字段 | 注入单个服务实例 | 必须是引用类型 |
|
||||||
|
| `[GetServices]` | 字段 | 注入服务集合 | IReadOnlyList\<T\> where T : class |
|
||||||
|
| `[GetAll]` | 类 | 自动推断并注入所有符合类型的字段 | 智能推断 |
|
||||||
|
|
||||||
|
## 基础使用
|
||||||
|
|
||||||
|
### 单实例注入
|
||||||
|
|
||||||
|
使用 `[GetModel]`、`[GetSystem]`、`[GetUtility]` 和 `[GetService]` 注入单个实例:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Abstractions.Utility;
|
||||||
|
|
||||||
|
[ContextAware]
|
||||||
|
public partial class PlayerController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _playerModel = null!;
|
||||||
|
|
||||||
|
[GetSystem]
|
||||||
|
private ICombatSystem _combatSystem = null!;
|
||||||
|
|
||||||
|
[GetUtility]
|
||||||
|
private IPathFinder _pathFinder = null!;
|
||||||
|
|
||||||
|
[GetService]
|
||||||
|
private IAudioService _audioService = null!;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
// 在首次使用注入字段前,先完成绑定
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
|
||||||
|
_combatSystem.Attack(_playerModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 集合注入
|
||||||
|
|
||||||
|
使用 `[GetModels]`、`[GetSystems]`、`[GetUtilities]` 和 `[GetServices]` 注入多个实例:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
[ContextAware]
|
||||||
|
public partial class StrategyManager
|
||||||
|
{
|
||||||
|
[GetModels]
|
||||||
|
private IReadOnlyList<IPlayerModel> _playerModels = null!;
|
||||||
|
|
||||||
|
[GetSystems]
|
||||||
|
private IReadOnlyList<IGameSystem> _gameSystems = null!;
|
||||||
|
|
||||||
|
public void ProcessAll()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
|
||||||
|
foreach (var system in _gameSystems)
|
||||||
|
{
|
||||||
|
system.Initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 智能推断注入 `[GetAll]`
|
||||||
|
|
||||||
|
`[GetAll]` 特性标记在类上,自动推断所有符合类型的字段并注入:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameManager
|
||||||
|
{
|
||||||
|
// 自动推断为 Model 注入
|
||||||
|
private IPlayerModel _playerModel = null!;
|
||||||
|
|
||||||
|
// 自动推断为 System 注入
|
||||||
|
private ICombatSystem _combatSystem = null!;
|
||||||
|
|
||||||
|
// 自动推断为 Utility 注入
|
||||||
|
private IPathFinder _pathFinder = null!;
|
||||||
|
|
||||||
|
// 自动推断为集合注入
|
||||||
|
private IReadOnlyList<IStrategy> _strategies = null!;
|
||||||
|
|
||||||
|
// Service 不会被自动推断(需要显式标记)
|
||||||
|
[GetService]
|
||||||
|
private IExternalService _externalService = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 生成代码解析
|
||||||
|
|
||||||
|
### 注入方法生成
|
||||||
|
|
||||||
|
生成器会为每个注入字段生成一个私有方法:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// <auto-generated />
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using GFramework.Core.Extensions;
|
||||||
|
|
||||||
|
namespace YourNamespace;
|
||||||
|
|
||||||
|
partial class PlayerController
|
||||||
|
{
|
||||||
|
private void __InjectContextBindings_Generated()
|
||||||
|
{
|
||||||
|
_playerModel = this.GetModel<global::IPlayerModel>();
|
||||||
|
_combatSystem = this.GetSystem<global::ICombatSystem>();
|
||||||
|
_pathFinder = this.GetUtility<global::IPathFinder>();
|
||||||
|
_audioService = this.GetService<global::IAudioService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 推荐调用时机与模式
|
||||||
|
|
||||||
|
生成器只负责生成 `__InjectContextBindings_Generated()` 方法,不会自动织入构造函数或生命周期回调。推荐统一遵循下面两条规则:
|
||||||
|
|
||||||
|
- 在上下文已经可用之后调用,例如 `Initialize()`、`OnEnter()`、Godot 的 `_Ready()` 或测试初始化阶段。
|
||||||
|
- 在首次读取任一注入字段之前调用。不要在构造函数体中假设这些字段已经可用。
|
||||||
|
|
||||||
|
推荐把调用集中放在单一的生命周期入口,而不是分散到构造函数、`_Ready()`、`Initialize()`
|
||||||
|
等多个位置。这样更容易保证字段只在“上下文已准备好且尚未被读取”时完成注入。
|
||||||
|
由于该方法是生成的私有成员,类外代码(包括测试)应复用类内部公开的生命周期入口,而不是直接调用生成方法。
|
||||||
|
|
||||||
|
#### 普通类型推荐模式
|
||||||
|
|
||||||
|
对普通控制器、服务协调器或策略管理器,优先在显式初始化方法开头调用:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
private ICombatSystem _combat = null!;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
_combat.Initialize(_player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Godot 节点推荐模式
|
||||||
|
|
||||||
|
对 `Node` 类型,优先在 `_Ready()` 开头调用,再使用注入字段:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class PlayerNode : Node
|
||||||
|
{
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
private IMovementSystem _movement = null!;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
_movement.Initialize(_model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 测试与手动装配模式
|
||||||
|
|
||||||
|
先配置上下文提供者,再调用与生产代码一致的公开入口,让入口内部完成注入:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Test]
|
||||||
|
public void TestController()
|
||||||
|
{
|
||||||
|
var testArchitecture = new TestArchitecture();
|
||||||
|
GameController.SetContextProvider(new TestContextProvider(testArchitecture));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var controller = new GameController();
|
||||||
|
controller.Initialize();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
GameController.ResetContextProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 智能推断规则
|
||||||
|
|
||||||
|
### 自动推断的类型
|
||||||
|
|
||||||
|
`[GetAll]` 会自动识别并注入以下类型:
|
||||||
|
|
||||||
|
| 字段类型 | 注入方式 | 生成代码示例 |
|
||||||
|
|---------------------------|-------|--------------------------|
|
||||||
|
| IModel 及其子类型 | 单实例注入 | `this.GetModel<T>()` |
|
||||||
|
| ISystem 及其子类型 | 单实例注入 | `this.GetSystem<T>()` |
|
||||||
|
| IUtility 及其子类型 | 单实例注入 | `this.GetUtility<T>()` |
|
||||||
|
| IReadOnlyList\<IModel\> | 集合注入 | `this.GetModels<T>()` |
|
||||||
|
| IReadOnlyList\<ISystem\> | 集合注入 | `this.GetSystems<T>()` |
|
||||||
|
| IReadOnlyList\<IUtility\> | 集合注入 | `this.GetUtilities<T>()` |
|
||||||
|
|
||||||
|
### 不自动推断的类型
|
||||||
|
|
||||||
|
以下类型不会自动推断,需要显式标记特性:
|
||||||
|
|
||||||
|
- **Service 类型**:任意引用类型太宽泛,需要使用 `[GetService]` 或 `[GetServices]` 显式标记
|
||||||
|
- **Godot.Node 及其子类**:避免与 Godot 引擎的节点注入冲突
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameNode : Node
|
||||||
|
{
|
||||||
|
// ✅ 自动注入
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
|
||||||
|
// ✅ 自动注入
|
||||||
|
private IGameSystem _system = null!;
|
||||||
|
|
||||||
|
// ❌ 不会自动注入(Service 需要显式标记)
|
||||||
|
[GetService]
|
||||||
|
private IAudioService _audio = null!;
|
||||||
|
|
||||||
|
// ❌ 不会自动注入(Godot.Node 被排除)
|
||||||
|
private Node _otherNode = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
### 控制器依赖注入
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.Core.Abstractions.Controller;
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameController : IController
|
||||||
|
{
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
private IEnemyModel _enemy = null!;
|
||||||
|
private ICombatSystem _combat = null!;
|
||||||
|
private IAudioSystem _audio = null!;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
_combat.Initialize(_player, _enemy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Attack()
|
||||||
|
{
|
||||||
|
_combat.Attack(_player, _enemy);
|
||||||
|
_audio.PlayAttackSound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 策略模式实现
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class StrategyManager
|
||||||
|
{
|
||||||
|
[GetServices]
|
||||||
|
private IReadOnlyList<IStrategy> _strategies = null!;
|
||||||
|
|
||||||
|
public IStrategy SelectBestStrategy()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
|
||||||
|
return _strategies.FirstOrDefault(s => s.CanExecute())
|
||||||
|
?? throw new InvalidOperationException("No strategy available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Godot 节点集成
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Godot;
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class PlayerNode : Node
|
||||||
|
{
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
private IMovementSystem _movement = null!;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
_movement.Initialize(_model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 诊断信息
|
||||||
|
|
||||||
|
### GF_ContextGet_001 - 不支持嵌套类
|
||||||
|
|
||||||
|
**错误信息**:`Class '{ClassName}' cannot use context Get injection inside a nested type`
|
||||||
|
|
||||||
|
**场景**:在嵌套类中使用注入特性
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class OuterClass
|
||||||
|
{
|
||||||
|
[GetModel] // ❌ 错误
|
||||||
|
private IModel _model = null!;
|
||||||
|
|
||||||
|
public partial class InnerClass // 嵌套类
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IModel _innerModel = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:避免在嵌套类中使用注入特性,将其提取为独立的类
|
||||||
|
|
||||||
|
### GF_ContextGet_002 - 不支持静态字段
|
||||||
|
|
||||||
|
**错误信息**:`Field '{FieldName}' cannot be static when using generated context Get injection`
|
||||||
|
|
||||||
|
**场景**:标记静态字段进行注入
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private static IPlayerModel _playerModel = null!; // ❌ 错误
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:改为实例字段
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _playerModel = null!; // ✅ 正确
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_ContextGet_003 - 不支持只读字段
|
||||||
|
|
||||||
|
**错误信息**:`Field '{FieldName}' cannot be readonly when using generated context Get injection`
|
||||||
|
|
||||||
|
**场景**:标记只读字段进行注入
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private readonly IPlayerModel _playerModel = null!; // ❌ 错误
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:移除 `readonly` 关键字
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _playerModel = null!; // ✅ 正确
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_ContextGet_004 - 字段类型不匹配
|
||||||
|
|
||||||
|
**错误信息**:`Field '{FieldName}' type '{FieldType}' is not valid for [{AttributeName}]`
|
||||||
|
|
||||||
|
**场景**:字段类型与注入特性要求的类型不匹配
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private ISystem _system = null!; // ❌ 错误:ISystem 不实现 IModel
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:确保字段类型符合特性要求
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _model = null!; // ✅ 正确
|
||||||
|
|
||||||
|
[GetSystem]
|
||||||
|
private ISystem _system = null!; // ✅ 正确
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_ContextGet_005 - 必须是上下文感知类型
|
||||||
|
|
||||||
|
**错误信息**:`Class '{ClassName}' must be context-aware to use generated context Get injection`
|
||||||
|
|
||||||
|
**场景**:类未标记 `[ContextAware]` 或未实现 `IContextAware`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public partial class GameController // ❌ 缺少 [ContextAware]
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:添加 `[ContextAware]` 特性或实现 `IContextAware` 接口
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware] // ✅ 正确
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_ContextGet_006 - 不支持多个注入特性
|
||||||
|
|
||||||
|
**错误信息**:`Field '{FieldName}' cannot declare multiple generated context Get attributes`
|
||||||
|
|
||||||
|
**场景**:同一字段标记了多个注入特性
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
[GetSystem] // ❌ 错误:多个特性冲突
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:每个字段只标记一个注入特性
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IPlayerModel _model = null!; // ✅ 正确
|
||||||
|
|
||||||
|
[GetSystem]
|
||||||
|
private ISystem _system = null!; // ✅ 正确
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 1. 优先使用 [GetAll]
|
||||||
|
|
||||||
|
使用 `[GetAll]` 可以减少特性标记,保持代码简洁:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 推荐:简洁
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class Controller
|
||||||
|
{
|
||||||
|
private ICoreModel _core = null!;
|
||||||
|
private ICoreSystem _system = null!;
|
||||||
|
private ICoreUtility _utility = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ 不推荐:冗余
|
||||||
|
[ContextAware]
|
||||||
|
public partial class Controller
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private ICoreModel _core = null!;
|
||||||
|
|
||||||
|
[GetSystem]
|
||||||
|
private ICoreSystem _system = null!;
|
||||||
|
|
||||||
|
[GetUtility]
|
||||||
|
private ICoreUtility _utility = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 为字段提供默认值
|
||||||
|
|
||||||
|
使用 `= null!` 或其他默认值避免编译警告:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class Controller
|
||||||
|
{
|
||||||
|
// ✅ 推荐:明确提供默认值
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
|
||||||
|
// ❌ 不推荐:会产生编译警告
|
||||||
|
private IPlayerModel _player;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 避免过度注入
|
||||||
|
|
||||||
|
只注入真正需要的依赖,保持类的职责单一:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 推荐:只注入必需的依赖
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class CombatController
|
||||||
|
{
|
||||||
|
private ICombatModel _combat = null!;
|
||||||
|
private ICombatSystem _system = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ 不推荐:注入过多依赖
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class MegaController
|
||||||
|
{
|
||||||
|
private IModel1 _m1 = null!;
|
||||||
|
private IModel2 _m2 = null!;
|
||||||
|
private IModel3 _m3 = null!;
|
||||||
|
// ... 10+ 个依赖,职责不清
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 显式标记 Service 注入
|
||||||
|
|
||||||
|
Service 类型不会自动推断,需要显式标记以保持意图清晰:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class ServiceManager
|
||||||
|
{
|
||||||
|
// 自动推断
|
||||||
|
private IPlayerModel _model = null!;
|
||||||
|
private IGameSystem _system = null!;
|
||||||
|
|
||||||
|
// Service 必须显式标记
|
||||||
|
[GetService]
|
||||||
|
private IExternalService _external = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 在上下文就绪后的生命周期入口中调用注入
|
||||||
|
|
||||||
|
不要把调用点分散到多个位置。优先选择一个明确的入口,例如 `Initialize()`、`Activate()` 或 `_Ready()`,并在方法开头完成注入:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
_player.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果构造函数执行时上下文尚未建立,过早注入会失败;即使在构造函数中调用了注入方法,也不要在调用之前访问这些字段。
|
||||||
|
|
||||||
|
## 高级场景
|
||||||
|
|
||||||
|
### 泛型类型支持
|
||||||
|
|
||||||
|
注入支持泛型类型字段:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class GenericController<TModel> where TModel : IModel
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private TModel _model = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 接口与实现类型
|
||||||
|
|
||||||
|
字段类型应使用接口而非具体实现:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class Controller
|
||||||
|
{
|
||||||
|
// ✅ 推荐:使用接口类型
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
|
||||||
|
// ❌ 不推荐:使用具体实现类型
|
||||||
|
private PlayerModel _player = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多架构场景
|
||||||
|
|
||||||
|
在多架构场景中,可以通过 `SetContextProvider` 切换架构:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameController
|
||||||
|
{
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
|
||||||
|
public static void SetArchitecture(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
// 切换架构提供者
|
||||||
|
SetContextProvider(new CustomContextProvider(architecture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q: 为什么 Service 不会自动推断?
|
||||||
|
|
||||||
|
**A**: Service 可以是任意引用类型,自动推断可能导致误注入。显式标记确保意图清晰,避免意外行为。
|
||||||
|
|
||||||
|
### Q: 可以在构造函数中使用注入的字段吗?
|
||||||
|
|
||||||
|
**A**: 一般不推荐。构造函数阶段通常不是最清晰的生命周期入口,而且在调用注入方法之前字段一定还不可用。推荐做法是在
|
||||||
|
`Initialize()`、`_Ready()` 等上下文已就绪的方法开头调用 `__InjectContextBindings_Generated()`,随后再使用这些字段。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class Controller
|
||||||
|
{
|
||||||
|
private IPlayerModel _player = null!;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
__InjectContextBindings_Generated();
|
||||||
|
_player.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: GetAll 会注入所有字段吗?
|
||||||
|
|
||||||
|
**A**: 不会。`[GetAll]` 只注入符合类型约束的字段:
|
||||||
|
|
||||||
|
- 实现 IModel、ISystem、IUtility 接口的类型
|
||||||
|
- 上述类型的 `IReadOnlyList<T>` 集合
|
||||||
|
- 排除 Godot.Node 类型
|
||||||
|
- 排除 Service 类型
|
||||||
|
|
||||||
|
### Q: 集合注入可以是 List\<T\> 吗?
|
||||||
|
|
||||||
|
**A**: 不可以。集合注入只支持 `IReadOnlyList<T>` 类型,这是为了保证注入集合的不可变性。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[ContextAware]
|
||||||
|
public partial class Controller
|
||||||
|
{
|
||||||
|
// ✅ 正确
|
||||||
|
[GetModels]
|
||||||
|
private IReadOnlyList<IPlayerModel> _players = null!;
|
||||||
|
|
||||||
|
// ❌ 错误:不支持
|
||||||
|
[GetModels]
|
||||||
|
private List<IPlayerModel> _players = null!;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 如何在测试中模拟注入?
|
||||||
|
|
||||||
|
**A**: 配合 `[ContextAware]` 的 `SetContextProvider` 方法,先建立测试上下文,再调用类内部已经封装好注入逻辑的公开入口:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Test]
|
||||||
|
public void TestController()
|
||||||
|
{
|
||||||
|
var testArchitecture = new TestArchitecture();
|
||||||
|
testArchitecture.RegisterModel<IPlayerModel>(new MockPlayerModel());
|
||||||
|
|
||||||
|
GameController.SetContextProvider(new TestContextProvider(testArchitecture));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var controller = new GameController();
|
||||||
|
controller.Initialize();
|
||||||
|
// 测试逻辑...
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
GameController.ResetContextProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [Source Generators 概述](./index.md)
|
||||||
|
- [ContextAware 生成器](./context-aware-generator.md)
|
||||||
|
- [架构组件](../core/architecture.md)
|
||||||
|
- [依赖注入](../core/ioc.md)
|
||||||
@ -86,6 +86,42 @@ public enum GameState
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 配置选项说明
|
||||||
|
|
||||||
|
#### 实际支持的选项
|
||||||
|
|
||||||
|
当前版本只支持以下配置选项:
|
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------------------|------|------|-------------------|
|
||||||
|
| GenerateIsMethods | bool | true | 是否为每个枚举值生成 IsX 方法 |
|
||||||
|
| GenerateIsInMethod | bool | true | 是否生成 IsIn 方法 |
|
||||||
|
|
||||||
|
#### 未实现的选项
|
||||||
|
|
||||||
|
以下选项在属性定义中存在,但生成器当前版本**未实现**:
|
||||||
|
|
||||||
|
- `GenerateHasMethod`:未实现 HasX 方法生成
|
||||||
|
- `IncludeToString`:未实现 ToString 扩展方法
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 以下选项不会生效
|
||||||
|
[GenerateEnumExtensions(
|
||||||
|
GenerateIsMethods = true,
|
||||||
|
GenerateIsInMethod = true,
|
||||||
|
GenerateHasMethod = true, // 未实现,参数会被忽略
|
||||||
|
IncludeToString = true // 未实现,参数会被忽略
|
||||||
|
)]
|
||||||
|
public enum GameState
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Paused,
|
||||||
|
GameOver
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:这些选项计划在后续版本中实现,届时会更新文档说明。
|
||||||
|
|
||||||
### 只生成 IsX 方法
|
### 只生成 IsX 方法
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
|||||||
@ -12,6 +12,8 @@ GFramework.SourceGenerators 是 GFramework 框架的源代码生成器包,通
|
|||||||
- [Log 属性生成器](#log-属性生成器)
|
- [Log 属性生成器](#log-属性生成器)
|
||||||
- [ContextAware 属性生成器](#contextaware-属性生成器)
|
- [ContextAware 属性生成器](#contextaware-属性生成器)
|
||||||
- [GenerateEnumExtensions 属性生成器](#generateenumextensions-属性生成器)
|
- [GenerateEnumExtensions 属性生成器](#generateenumextensions-属性生成器)
|
||||||
|
- [Priority 属性生成器](#priority-属性生成器)
|
||||||
|
- [Context Get 注入生成器](#context-get-注入生成器)
|
||||||
- [诊断信息](#诊断信息)
|
- [诊断信息](#诊断信息)
|
||||||
- [性能优势](#性能优势)
|
- [性能优势](#性能优势)
|
||||||
- [使用示例](#使用示例)
|
- [使用示例](#使用示例)
|
||||||
@ -36,6 +38,8 @@ GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译
|
|||||||
- **[Log] 属性**:自动生成 ILogger 字段和日志方法
|
- **[Log] 属性**:自动生成 ILogger 字段和日志方法
|
||||||
- **[ContextAware] 属性**:自动实现 IContextAware 接口
|
- **[ContextAware] 属性**:自动实现 IContextAware 接口
|
||||||
- **[GenerateEnumExtensions] 属性**:自动生成枚举扩展方法
|
- **[GenerateEnumExtensions] 属性**:自动生成枚举扩展方法
|
||||||
|
- **[Priority] 属性**:自动实现 IPrioritized 接口,为类添加优先级标记
|
||||||
|
- **Context Get 注入特性**:自动注入架构组件(GetModel/GetSystem/GetUtility/GetService/GetAll)
|
||||||
|
|
||||||
### 🔧 高级特性
|
### 🔧 高级特性
|
||||||
|
|
||||||
@ -43,6 +47,8 @@ GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译
|
|||||||
- **增量编译**:只生成修改过的代码,提高编译速度
|
- **增量编译**:只生成修改过的代码,提高编译速度
|
||||||
- **命名空间控制**:灵活控制生成代码的命名空间
|
- **命名空间控制**:灵活控制生成代码的命名空间
|
||||||
- **可访问性控制**:支持不同的访问修饰符
|
- **可访问性控制**:支持不同的访问修饰符
|
||||||
|
- **智能推断注入**:[GetAll] 自动推断字段类型并注入架构组件
|
||||||
|
- **优先级排序建议**:分析器自动建议使用 GetAllByPriority 确保正确排序
|
||||||
|
|
||||||
## 安装配置
|
## 安装配置
|
||||||
|
|
||||||
@ -453,6 +459,38 @@ public enum ConflictEnum
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Priority 相关诊断
|
||||||
|
|
||||||
|
`Priority` 相关规则以专用文档为权威来源:
|
||||||
|
|
||||||
|
- 完整生成器诊断见 [Priority 生成器](./priority-generator.md#诊断信息)
|
||||||
|
- 排序分析器规则见 [与 PriorityUsageAnalyzer 集成](./priority-generator.md#与-priorityusageanalyzer-集成)
|
||||||
|
|
||||||
|
| 诊断 ID | 含义 | 常见修复方向 |
|
||||||
|
|-------------------------|-------------------------------|------------------------------|
|
||||||
|
| `GF_Priority_001` | `[Priority]` 只能应用于类 | 仅在 `partial class` 上使用特性 |
|
||||||
|
| `GF_Priority_002` | 类型已手动实现 `IPrioritized` | 删除特性或删除手写接口实现 |
|
||||||
|
| `GF_Priority_003` | 标记类型未声明为 `partial` | 为类型添加 `partial` |
|
||||||
|
| `GF_Priority_004` | 优先级参数无效 | 提供有效的整数优先级值 |
|
||||||
|
| `GF_Priority_005` | 嵌套类不支持生成 | 将目标类型提取为顶层类 |
|
||||||
|
| `GF_Priority_Usage_001` | 应优先使用 `GetAllByPriority<T>()` | 对实现 `IPrioritized` 的类型改用排序获取 |
|
||||||
|
|
||||||
|
### Context Get 相关诊断
|
||||||
|
|
||||||
|
`Context Get` 相关规则以专用文档为权威来源:
|
||||||
|
|
||||||
|
- 完整诊断见 [Context Get 注入生成器](./context-get-generator.md#诊断信息)
|
||||||
|
- 调用时机建议见 [推荐调用时机与模式](./context-get-generator.md#推荐调用时机与模式)
|
||||||
|
|
||||||
|
| 诊断 ID | 含义 | 常见修复方向 |
|
||||||
|
|---------------------|--------------------|---------------------------------------------------------------|
|
||||||
|
| `GF_ContextGet_001` | 嵌套类不支持生成注入 | 将目标类型提取为顶层类 |
|
||||||
|
| `GF_ContextGet_002` | 注入字段不能为 `static` | 改为实例字段 |
|
||||||
|
| `GF_ContextGet_003` | 注入字段不能为 `readonly` | 移除 `readonly` |
|
||||||
|
| `GF_ContextGet_004` | 字段类型与注入特性不匹配 | 使用符合特性约束的字段类型 |
|
||||||
|
| `GF_ContextGet_005` | 目标类型必须具备上下文访问能力 | 添加 `[ContextAware]`、实现 `IContextAware` 或继承 `ContextAwareBase` |
|
||||||
|
| `GF_ContextGet_006` | 同一字段不能声明多个注入特性 | 每个字段只保留一个注入特性 |
|
||||||
|
|
||||||
## 性能优势
|
## 性能优势
|
||||||
|
|
||||||
### 编译时 vs 运行时对比
|
### 编译时 vs 运行时对比
|
||||||
@ -895,4 +933,4 @@ graph TD
|
|||||||
- **.NET**: 6.0+
|
- **.NET**: 6.0+
|
||||||
- **Visual Studio**: 2022 17.0+
|
- **Visual Studio**: 2022 17.0+
|
||||||
- **Rider**: 2022.3+
|
- **Rider**: 2022.3+
|
||||||
- **Roslyn**: 4.0+
|
- **Roslyn**: 4.0+
|
||||||
|
|||||||
747
docs/zh-CN/source-generators/priority-generator.md
Normal file
747
docs/zh-CN/source-generators/priority-generator.md
Normal file
@ -0,0 +1,747 @@
|
|||||||
|
# Priority 生成器
|
||||||
|
|
||||||
|
> 自动实现 IPrioritized 接口,为类添加优先级标记
|
||||||
|
|
||||||
|
Priority 生成器通过源代码生成器自动实现 `IPrioritized` 接口,简化优先级标记和排序逻辑的实现。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
### 核心功能
|
||||||
|
|
||||||
|
- **自动实现接口**:自动实现 `IPrioritized` 接口的 `Priority` 属性
|
||||||
|
- **优先级标记**:通过特性参数指定优先级值
|
||||||
|
- **编译时生成**:在编译时生成代码,零运行时开销
|
||||||
|
- **类型安全**:编译时类型检查,避免运行时错误
|
||||||
|
|
||||||
|
### 适用场景
|
||||||
|
|
||||||
|
- 系统初始化顺序控制
|
||||||
|
- 事件处理器优先级排序
|
||||||
|
- 服务注册顺序管理
|
||||||
|
- 需要按优先级排序的任何场景
|
||||||
|
|
||||||
|
## 基础使用
|
||||||
|
|
||||||
|
### 标记优先级
|
||||||
|
|
||||||
|
使用 `[Priority]` 特性为类标记优先级:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Bases;
|
||||||
|
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class MySystem
|
||||||
|
{
|
||||||
|
// 自动生成 IPrioritized 接口实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 生成代码
|
||||||
|
|
||||||
|
编译器会自动生成如下代码:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// <auto-generated/>
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace YourNamespace;
|
||||||
|
|
||||||
|
partial class MySystem : global::GFramework.Core.Abstractions.Bases.IPrioritized
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取优先级值: 10
|
||||||
|
/// </summary>
|
||||||
|
public int Priority => 10;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用生成的优先级
|
||||||
|
|
||||||
|
生成的 `Priority` 属性可用于排序:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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` 预定义常量来标记优先级:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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 常量定义**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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; // 延迟:清理、统计等
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
### 系统初始化顺序
|
||||||
|
|
||||||
|
控制系统初始化的顺序,确保依赖关系正确:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
// 初始化音频引擎
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在架构中按优先级初始化:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void InitSystems()
|
||||||
|
{
|
||||||
|
// 获取所有系统并按优先级排序
|
||||||
|
var systems = this.GetAllByPriority<ISystem>();
|
||||||
|
|
||||||
|
foreach (var system in systems)
|
||||||
|
{
|
||||||
|
system.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件处理器优先级
|
||||||
|
|
||||||
|
控制事件处理器的执行顺序:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 记录日志
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
事件总线按优先级调用处理器:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 服务排序
|
||||||
|
|
||||||
|
控制多个服务实现的优先级:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 高优先级服务
|
||||||
|
[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>()` 的场景:
|
||||||
|
|
||||||
|
**错误示例**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 不推荐:可能未按优先级排序
|
||||||
|
var systems = context.GetAll<ISystem>();
|
||||||
|
```
|
||||||
|
|
||||||
|
**正确示例**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 推荐:确保按优先级排序
|
||||||
|
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]` 特性应用于非类类型
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public interface IMyInterface // ❌ 错误
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Priority(10)]
|
||||||
|
public struct MyStruct // ❌ 错误
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:只在类上使用 `[Priority]` 特性
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class MyClass // ✅ 正确
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_Priority_002 - 已实现 IPrioritized 接口
|
||||||
|
|
||||||
|
**错误信息**:`Type '{ClassName}' already implements IPrioritized interface`
|
||||||
|
|
||||||
|
**场景**:类已手动实现 `IPrioritized` 接口
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class MySystem : IPrioritized // ❌ 冲突
|
||||||
|
{
|
||||||
|
public int Priority => 10; // 手动实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:移除 `[Priority]` 特性或移除手动实现
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 方案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`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public class MySystem // ❌ 缺少 partial
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:添加 `partial` 关键字
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class MySystem // ✅ 正确
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_Priority_004 - 优先级值无效
|
||||||
|
|
||||||
|
**错误信息**:`Priority value is invalid`
|
||||||
|
|
||||||
|
**场景**:特性参数无效或未提供
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority] // ❌ 缺少参数
|
||||||
|
public partial class MySystem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:提供有效的优先级值
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)] // ✅ 正确
|
||||||
|
public partial class MySystem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GF_Priority_005 - 不支持嵌套类
|
||||||
|
|
||||||
|
**错误信息**:`Nested class '{ClassName}' is not supported`
|
||||||
|
|
||||||
|
**场景**:在嵌套类中使用 `[Priority]` 特性
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public partial class OuterClass
|
||||||
|
{
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class InnerClass // ❌ 错误
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:将嵌套类提取为独立的类
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class InnerClass // ✅ 正确
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 1. 使用 PriorityGroup 常量
|
||||||
|
|
||||||
|
避免使用魔法数字,优先使用预定义常量:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 推荐:使用常量
|
||||||
|
[Priority(PriorityGroup.Critical)]
|
||||||
|
public partial class InputSystem { }
|
||||||
|
|
||||||
|
// ❌ 不推荐:魔法数字
|
||||||
|
[Priority(-100)]
|
||||||
|
public partial class InputSystem { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 预留优先级间隔
|
||||||
|
|
||||||
|
为未来扩展预留间隔:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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. 文档化优先级语义
|
||||||
|
|
||||||
|
为自定义优先级值添加注释:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// 输入系统,优先级 -100,需要最先初始化以接收输入事件
|
||||||
|
/// </summary>
|
||||||
|
[Priority(PriorityGroup.Critical)]
|
||||||
|
public partial class InputSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 避免优先级冲突
|
||||||
|
|
||||||
|
当多个类有相同优先级时,执行顺序不确定。应避免依赖特定顺序:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 不推荐:相同优先级,顺序不确定
|
||||||
|
[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. 只在真正需要排序的场景使用
|
||||||
|
|
||||||
|
不要滥用优先级特性,只在确实需要排序的场景使用:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 推荐:需要排序的系统
|
||||||
|
[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 特性支持泛型类:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(20)]
|
||||||
|
public partial class GenericSystem<T> : ISystem
|
||||||
|
{
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
// 泛型系统的初始化
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 与其他特性组合
|
||||||
|
|
||||||
|
Priority 可以与其他源代码生成器特性组合使用:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 运行时优先级查询
|
||||||
|
|
||||||
|
可以在运行时查询优先级值:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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` 接口。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class DynamicPrioritySystem : IPrioritized
|
||||||
|
{
|
||||||
|
private int _priority;
|
||||||
|
|
||||||
|
public int Priority => _priority;
|
||||||
|
|
||||||
|
public void SetPriority(int value)
|
||||||
|
{
|
||||||
|
_priority = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 优先级支持继承吗?
|
||||||
|
|
||||||
|
**A**: `[Priority]` 特性标记为 `Inherited = false`,不会被子类继承。每个子类需要独立标记优先级。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Priority(10)]
|
||||||
|
public partial class BaseSystem { }
|
||||||
|
|
||||||
|
public partial class DerivedSystem : BaseSystem // 不会继承 Priority = 10
|
||||||
|
{
|
||||||
|
// 需要重新标记
|
||||||
|
// [Priority(20)]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 如何在不使用特性的情况下实现优先级?
|
||||||
|
|
||||||
|
**A**: 可以手动实现 `IPrioritized` 接口:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ManualPrioritySystem : IPrioritized
|
||||||
|
{
|
||||||
|
public int Priority => 10;
|
||||||
|
|
||||||
|
// 手动实现提供更大的灵活性
|
||||||
|
public int Priority => _config.Enabled ? 10 : 100;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 负数优先级和正数优先级的区别是什么?
|
||||||
|
|
||||||
|
**A**: 优先级值用于排序,通常值越小优先级越高:
|
||||||
|
|
||||||
|
- 负数(-100):高优先级,最先执行
|
||||||
|
- 零(0):默认优先级
|
||||||
|
- 正数(100):低优先级,最后执行
|
||||||
|
|
||||||
|
具体含义取决于使用场景,但推荐遵循 `PriorityGroup` 定义的语义。
|
||||||
|
|
||||||
|
## 实际应用示例
|
||||||
|
|
||||||
|
### 游戏系统架构
|
||||||
|
|
||||||
|
完整的游戏系统初始化示例:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在架构中初始化:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override async Task InitAsync()
|
||||||
|
{
|
||||||
|
// 获取所有系统并按优先级排序
|
||||||
|
var systems = this.GetAllByPriority<ISystem>();
|
||||||
|
|
||||||
|
foreach (var system in systems)
|
||||||
|
{
|
||||||
|
await system.InitAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [Source Generators 概述](./index.md)
|
||||||
|
- [ContextAware 生成器](./context-aware-generator.md)
|
||||||
|
- [系统初始化](../core/system.md)
|
||||||
Loading…
x
Reference in New Issue
Block a user