GeWuYou 1ab615db26 docs(source-generators): 更新文档结构并调整Godot版本信息
- 将Source Generators概述文档从overview重命名为index
- 更新所有相关文档中的链接指向新的index文件
- 移除Godot文档中的许可证和版本信息冗余内容
- 统一Godot版本要求从4.5.1+到4.6
- 删除各模块文档末尾的相关链接部分
- 优化文档结构提升可读性
2026-02-11 15:57:40 +08:00

992 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GFramework.SourceGenerators
> 编译时代码生成 - 零运行时开销的代码增强工具
GFramework.SourceGenerators 是 GFramework 框架的源代码生成器包,通过编译时分析自动生成样板代码,显著提升开发效率并减少运行时开销。
## 📋 目录
- [概述](#概述)
- [核心特性](#核心特性)
- [安装配置](#安装配置)
- [Log 属性生成器](#log-属性生成器)
- [ContextAware 属性生成器](#contextaware-属性生成器)
- [GenerateEnumExtensions 属性生成器](#generateenumextensions-属性生成器)
- [诊断信息](#诊断信息)
- [性能优势](#性能优势)
- [使用示例](#使用示例)
- [最佳实践](#最佳实践)
- [常见问题](#常见问题)
## 概述
GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译时分析你的代码并自动生成常用的样板代码,让开发者专注于业务逻辑而不是重复的模板代码。
### 核心设计理念
- **零运行时开销**:代码在编译时生成,无反射或动态调用
- **类型安全**:编译时类型检查,避免运行时错误
- **开发效率**:自动生成样板代码,减少重复工作
- **可配置性**:支持多种配置选项满足不同需求
## 核心特性
### 🎯 主要生成器
- **[Log] 属性**:自动生成 ILogger 字段和日志方法
- **[ContextAware] 属性**:自动实现 IContextAware 接口
- **[GenerateEnumExtensions] 属性**:自动生成枚举扩展方法
### 🔧 高级特性
- **智能诊断**:生成器包含详细的错误诊断信息
- **增量编译**:只生成修改过的代码,提高编译速度
- **命名空间控制**:灵活控制生成代码的命名空间
- **可访问性控制**:支持不同的访问修饰符
## 安装配置
### NuGet 包安装
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GeWuYou.GFramework.SourceGenerators" Version="1.0.0"/>
<PackageReference Include="GeWuYou.GFramework.SourceGenerators.Attributes" Version="1.0.0"/>
</ItemGroup>
</Project>
```
### 项目文件配置
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
</Project>
```
## Log 属性生成器
[Log] 属性自动为标记的类生成日志记录功能,包括 ILogger 字段和便捷的日志方法。
### 基础使用
```csharp
using GFramework.SourceGenerators.Attributes;
[Log]
public partial class PlayerController
{
public void DoSomething()
{
Logger.Info("Doing something"); // 自动生成的 Logger 字段
Logger.Debug("Debug information");
Logger.Warning("Warning message");
Logger.Error("Error occurred");
}
}
```
### 生成的代码
编译器会自动生成如下代码:
```csharp
// <auto-generated/>
using Microsoft.Extensions.Logging;
namespace YourNamespace
{
public partial class PlayerController
{
private static readonly ILogger Logger =
LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger<PlayerController>();
}
}
```
### 高级配置
```csharp
[Log(
fieldName = "CustomLogger", // 自定义字段名
accessModifier = AccessModifier.Public, // 访问修饰符
isStatic = false, // 是否为静态字段
loggerName = "Custom.PlayerLogger", // 自定义日志器名称
includeLoggerInterface = true // 是否包含 ILogger 接口
)]
public partial class CustomLoggerExample
{
public void LogSomething()
{
CustomLogger.LogInformation("Custom logger message");
}
}
```
### 配置选项说明
| 参数 | 类型 | 默认值 | 说明 |
|--------------------------|----------------|----------|---------------------|
| `fieldName` | string | "Logger" | 生成的日志字段名称 |
| `accessModifier` | AccessModifier | Private | 字段访问修饰符 |
| `isStatic` | bool | true | 是否生成静态字段 |
| `loggerName` | string | null | 自定义日志器名称null 时使用类名 |
| `includeLoggerInterface` | bool | false | 是否包含 ILogger 接口实现 |
### 静态类支持
```csharp
[Log]
public static partial class MathHelper
{
public static int Add(int a, int b)
{
Logger.Debug($"Adding {a} and {b}");
return a + b;
}
}
```
### 日志级别控制
```csharp
[Log(minLevel = LogLevel.Warning)]
public partial class WarningOnlyLogger
{
public void ProcessData()
{
Logger.Debug("This won't be logged"); // 低于最小级别,被过滤
Logger.Warning("This will be logged");
Logger.Error("This will also be logged");
}
}
```
## ContextAware 属性生成器
[ContextAware] 属性自动实现 IContextAware 接口,提供便捷的架构上下文访问能力。
### 基础使用
```csharp
using GFramework.SourceGenerators.Attributes;
using GFramework.Core.Abstractions;
[ContextAware]
public partial class PlayerController : IController
{
public void Initialize()
{
// Context 属性自动生成,提供架构上下文访问
var playerModel = Context.GetModel<PlayerModel>();
var combatSystem = Context.GetSystem<CombatSystem>();
Context.SendEvent(new PlayerInitializedEvent());
}
}
```
### 生成的代码
编译器会自动生成如下代码:
```csharp
// <auto-generated/>
using GFramework.Core.Abstractions;
namespace YourNamespace
{
public partial class PlayerController : IContextAware
{
private IContextAware.Context _context;
public IContextAware.Context Context => _context ??= new LazyContext(this);
public void SetContext(IContextAware.Context context)
{
_context = context;
}
public IContextAware.Context GetContext()
{
return _context;
}
}
}
```
### 延迟初始化
```csharp
[ContextAware(useLazy = true)]
public partial class LazyContextExample
{
public void AccessContext()
{
// Context 会延迟初始化,直到第一次访问
var model = Context.GetModel<SomeModel>();
}
}
```
### 上下文验证
```csharp
[ContextAware(validateContext = true)]
public partial class ValidatedContextExample
{
public void AccessContext()
{
// 每次访问都会验证上下文的有效性
var model = Context.GetModel<SomeModel>();
if (Context.IsInvalid)
{
throw new InvalidOperationException("Context is invalid");
}
}
}
```
### 与其他属性组合
```csharp
[Log]
[ContextAware]
public partial class AdvancedController : IController
{
public void ProcessRequest()
{
Logger.Info("Processing request");
var model = Context.GetModel<PlayerModel>();
Logger.Info($"Player health: {model.Health}");
Context.SendCommand(new ProcessCommand());
Logger.Debug("Command sent");
}
}
```
## GenerateEnumExtensions 属性生成器
[GenerateEnumExtensions] 属性为枚举类型生成便捷的扩展方法,提高代码可读性和类型安全性。
### 基础使用
```csharp
using GFramework.SourceGenerators.Attributes;
[GenerateEnumExtensions]
public enum GameState
{
Playing,
Paused,
GameOver,
Menu
}
// 自动生成的扩展方法:
public static class GameStateExtensions
{
public static bool IsPlaying(this GameState state) => state == GameState.Playing;
public static bool IsPaused(this GameState state) => state == GameState.Paused;
public static bool IsGameOver(this GameState state) => state == GameState.GameOver;
public static bool IsMenu(this GameState state) => state == GameState.Menu;
public static bool IsIn(this GameState state, params GameState[] values)
{
return values.Contains(state);
}
}
// 使用示例
public class GameManager
{
private GameState _currentState = GameState.Menu;
public bool CanProcessInput()
{
return _currentState.IsPlaying() || _currentState.IsMenu();
}
public bool IsGameOver()
{
return _currentState.IsGameOver();
}
public bool IsActiveState()
{
return _currentState.IsIn(GameState.Playing, GameState.Menu);
}
}
```
### 自定义扩展方法
```csharp
[GenerateEnumExtensions(
generateIsMethods = true,
generateHasMethod = true,
generateInMethod = true,
customPrefix = "Is",
includeToString = true
)]
public enum PlayerState
{
Idle,
Walking,
Running,
Jumping,
Attacking
}
// 生成更多扩展方法
public static class PlayerStateExtensions
{
public static bool IsIdle(this PlayerState state) => state == PlayerState.Idle;
public static bool IsWalking(this PlayerState state) => state == PlayerState.Walking;
public static bool IsRunning(this PlayerState state) => state == PlayerState.Running;
public static bool IsJumping(this PlayerState state) => state == PlayerState.Jumping;
public static bool IsAttacking(this PlayerState state) => state == PlayerState.Attacking;
public static bool HasIdle(this PlayerState state) => state == PlayerState.Idle;
public static bool HasWalking(this PlayerState state) => state == PlayerState.Walking;
// ... 其他 Has 方法
public static bool In(this PlayerState state, params PlayerState[] values)
{
return values.Contains(state);
}
public static string ToDisplayString(this PlayerState state)
{
return state switch
{
PlayerState.Idle => "Idle",
PlayerState.Walking => "Walking",
PlayerState.Running => "Running",
PlayerState.Jumping => "Jumping",
PlayerState.Attacking => "Attacking",
_ => state.ToString()
};
}
}
```
### 位标志枚举支持
```csharp
[GenerateEnumExtensions]
[Flags]
public enum PlayerAbilities
{
None = 0,
Jump = 1 << 0,
Run = 1 << 1,
Attack = 1 << 2,
Defend = 1 << 3,
Magic = 1 << 4
}
// 生成位标志扩展方法
public static class PlayerAbilitiesExtensions
{
public static bool HasJump(this PlayerAbilities abilities) => abilities.HasFlag(PlayerAbilities.Jump);
public static bool HasRun(this PlayerAbilities abilities) => abilities.HasFlag(PlayerAbilities.Run);
// ... 其他位标志方法
public static bool HasAny(this PlayerAbilities abilities, params PlayerAbilities[] flags)
{
return flags.Any(flag => abilities.HasFlag(flag));
}
public static bool HasAll(this PlayerAbilities abilities, params PlayerAbilities[] flags)
{
return flags.All(flag => abilities.HasFlag(flag));
}
}
```
### 配置选项说明
| 参数 | 类型 | 默认值 | 说明 |
|---------------------|--------|-------|------------------------|
| `generateIsMethods` | bool | true | 是否生成 IsX() 方法 |
| `generateHasMethod` | bool | true | 是否生成 HasX() 方法 |
| `generateInMethod` | bool | true | 是否生成 In(params T[]) 方法 |
| `customPrefix` | string | "Is" | 方法名前缀 |
| `includeToString` | bool | false | 是否生成 ToString 扩展 |
| `namespace` | string | null | 生成扩展类的命名空间 |
## 诊断信息
GFramework.SourceGenerators 提供详细的编译时诊断信息,帮助开发者快速定位和解决问题。
### GF_Logging_001 - 日志字段名冲突
```csharp
[Log(fieldName = "Logger")]
public partial class ClassWithLogger
{
private readonly ILogger Logger; // ❌ 冲突!
}
```
**错误信息**: `GF_Logging_001: Logger field name 'Logger' conflicts with existing field`
**解决方案**: 更改字段名或移除冲突字段
```csharp
[Log(fieldName = "CustomLogger")]
public partial class ClassWithLogger
{
private readonly ILogger Logger; // ✅ 不冲突
private static readonly ILogger CustomLogger; // ✅ 生成器使用 CustomLogger
}
```
### GF_Rule_001 - ContextAware 接口冲突
```csharp
[ContextAware]
public partial class AlreadyContextAware : IContextAware // ❌ 冲突!
{
// 已实现 IContextAware
}
```
**错误信息**: `GF_Rule_001: Type already implements IContextAware interface`
**解决方案**: 移除 [ContextAware] 属性或移除手动实现
```csharp
// 方案1移除属性
public partial class AlreadyContextAware : IContextAware
{
// 手动实现
}
// 方案2移除手动实现使用生成器
[ContextAware]
public partial class AlreadyContextAware
{
// 生成器自动实现
}
```
### GF_Enum_001 - 枚举成员命名冲突
```csharp
[GenerateEnumExtensions]
public enum ConflictEnum
{
IsPlaying, // ❌ 冲突!会生成 IsIsPlaying()
HasJump // ❌ 冲突!会生成 HasHasJump()
}
```
**错误信息**: `GF_Enum_001: Enum member name conflicts with generated method`
**解决方案**: 重命名枚举成员或自定义前缀
```csharp
[GenerateEnumExtensions(customPrefix = "IsState")]
public enum ConflictEnum
{
Playing, // ✅ 生成 IsStatePlaying()
Jump // ✅ 生成 IsStateJump()
}
```
## 性能优势
### 编译时 vs 运行时对比
| 特性 | 手动实现 | 反射实现 | 源码生成器 |
|-----------|------|------|-------|
| **运行时性能** | 最优 | 最差 | 最优 |
| **内存开销** | 最小 | 最大 | 最小 |
| **类型安全** | 编译时 | 运行时 | 编译时 |
| **开发效率** | 低 | 中 | 高 |
| **调试友好** | 好 | 差 | 好 |
### 基准测试结果
```csharp
// 日志性能对比 (100,000 次调用)
// 手动实现: 0.23ms
// 反射实现: 45.67ms
// 源码生成器: 0.24ms (几乎无差异)
// 上下文访问性能对比 (1,000,000 次访问)
// 手动实现: 0.12ms
// 反射实现: 23.45ms
// 源码生成器: 0.13ms (几乎无差异)
```
### 内存分配分析
```csharp
// 使用 source generators 的内存分配
[Log]
[ContextAware]
public partial class EfficientController : IController
{
public void Process()
{
Logger.Info("Processing"); // 0 分配
var model = Context.GetModel<PlayerModel>(); // 0 分配
}
}
// 对比反射实现的内存分配
public class InefficientController : IController
{
public void Process()
{
var logger = GetLoggerViaReflection(); // 每次分配
var model = GetModelViaReflection<PlayerModel>(); // 每次分配
}
}
```
## 使用示例
### 完整的游戏控制器示例
```csharp
using GFramework.SourceGenerators.Attributes;
using GFramework.Core.Abstractions;
[Log]
[ContextAware]
public partial class GameController : Node, IController
{
private PlayerModel _playerModel;
private CombatSystem _combatSystem;
public override void _Ready()
{
// 初始化模型和系统引用
_playerModel = Context.GetModel<PlayerModel>();
_combatSystem = Context.GetSystem<CombatSystem>();
// 监听事件
this.RegisterEvent<PlayerInputEvent>(OnPlayerInput)
.UnRegisterWhenNodeExitTree(this);
Logger.Info("Game controller initialized");
}
private void OnPlayerInput(PlayerInputEvent e)
{
Logger.Debug($"Processing player input: {e.Action}");
switch (e.Action)
{
case "attack":
HandleAttack();
break;
case "defend":
HandleDefend();
break;
}
}
private void HandleAttack()
{
if (_playerModel.CanAttack())
{
Logger.Info("Player attacks");
_combatSystem.ProcessAttack();
Context.SendEvent(new AttackEvent());
}
else
{
Logger.Warning("Player cannot attack - cooldown");
}
}
private void HandleDefend()
{
if (_playerModel.CanDefend())
{
Logger.Info("Player defends");
_playerModel.IsDefending.Value = true;
Context.SendEvent(new DefendEvent());
}
else
{
Logger.Warning("Player cannot defend");
}
}
}
```
### 枚举状态管理示例
```csharp
[GenerateEnumExtensions(
generateIsMethods = true,
generateHasMethod = true,
generateInMethod = true,
includeToString = true
)]
public enum CharacterState
{
Idle,
Walking,
Running,
Jumping,
Falling,
Attacking,
Hurt,
Dead
}
[Log]
[ContextAware]
public partial class CharacterController : Node, IController
{
private CharacterModel _characterModel;
public override void _Ready()
{
_characterModel = Context.GetModel<CharacterModel>();
// 监听状态变化
_characterModel.State.Register(OnStateChanged);
}
private void OnStateChanged(CharacterState newState)
{
Logger.Info($"Character state changed to: {newState.ToDisplayString()}");
// 使用生成的扩展方法
if (newState.IsDead())
{
HandleDeath();
}
else if (newState.IsHurt())
{
HandleHurt();
}
else if (newState.In(CharacterState.Walking, CharacterState.Running))
{
StartMovementEffects();
}
// 检查是否可以接受输入
if (newState.In(CharacterState.Idle, CharacterState.Walking, CharacterState.Running))
{
EnableInput();
}
else
{
DisableInput();
}
}
private bool CanAttack()
{
var state = _characterModel.State.Value;
return state.In(CharacterState.Idle, CharacterState.Walking, CharacterState.Running);
}
private void HandleDeath()
{
Logger.Info("Character died");
DisableInput();
PlayDeathAnimation();
Context.SendEvent(new CharacterDeathEvent());
}
}
```
## 最佳实践
### 🎯 属性使用策略
#### 1. 合理的属性组合
```csharp
// 好的做法:相关功能组合使用
[Log]
[ContextAware]
public partial class BusinessLogicComponent : IComponent
{
// 既有日志记录又有上下文访问
}
// 避免:不必要的属性
[Log] // ❌ 静态工具类通常不需要日志
public static class MathHelper
{
public static int Add(int a, int b) => a + b;
}
```
#### 2. 命名约定
```csharp
// 好的做法:一致的命名
[Log(fieldName = "Logger")]
public partial class PlayerController { }
[Log(fieldName = "Logger")]
public partial class EnemyController { }
// 避免:不一致的命名
[Log(fieldName = "Logger")]
public partial class PlayerController { }
[Log(fieldName = "CustomLogger")]
public partial class EnemyController { }
```
### 🏗️ 项目组织
#### 1. 分离生成器和业务逻辑
```csharp
// 好的做法:部分类分离
// PlayerController.Logic.cs - 业务逻辑
public partial class PlayerController : IController
{
public void Move(Vector2 direction)
{
if (CanMove())
{
UpdatePosition(direction);
Logger.Debug($"Player moved to {direction}");
}
}
}
// PlayerController.Generated.cs - 生成代码所在
// 不需要手动维护,由生成器处理
```
#### 2. 枚举设计
```csharp
// 好的做法:有意义的枚举设计
[GenerateEnumExtensions]
public enum GameState
{
MainMenu, // 主菜单
Playing, // 游戏中
Paused, // 暂停
GameOver, // 游戏结束
Victory // 胜利
}
// 避免:含义不明确的枚举值
[GenerateEnumExtensions]
public enum State
{
State1,
State2,
State3
}
```
### 🔧 性能优化
#### 1. 避免过度日志
```csharp
// 好的做法:合理的日志级别
[Log(minLevel = LogLevel.Information)]
public partial class PerformanceCriticalComponent
{
public void Update()
{
// 只在必要时记录日志
if (_performanceCounter % 1000 == 0)
{
Logger.Debug($"Performance: {_performanceCounter} ticks");
}
}
}
// 避免:过度日志记录
[Log(minLevel = LogLevel.Debug)]
public partial class NoisyComponent
{
public void Update()
{
Logger.Debug($"Frame: {Engine.GetProcessFrames()}"); // 太频繁
}
}
```
#### 2. 延迟上下文初始化
```csharp
// 好的做法:延迟初始化
[ContextAware(useLazy = true)]
public partial class LazyContextComponent : IComponent
{
// 只有在第一次访问 Context 时才会初始化
public void Initialize()
{
// 如果这里不需要 Context就不会初始化
SomeOtherInitialization();
}
}
```
### 🛡️ 错误处理
#### 1. 上下文验证
```csharp
[ContextAware(validateContext = true)]
public partial class SafeContextComponent : IComponent
{
public void ProcessData()
{
if (Context.IsInvalid)
{
Logger.Error("Context is invalid, cannot process data");
return;
}
// 安全地使用 Context
var model = Context.GetModel<DataModel>();
// ...
}
}
```
#### 2. 异常处理配合
```csharp
[Log]
[ContextAware]
public partial class RobustComponent : IComponent
{
public void RiskyOperation()
{
try
{
var model = Context.GetModel<RiskyModel>();
model.PerformRiskyOperation();
Logger.Info("Operation completed successfully");
}
catch (Exception ex)
{
Logger.Error($"Operation failed: {ex.Message}");
Context.SendEvent(new OperationFailedEvent { Error = ex.Message });
}
}
}
```
## 常见问题
### Q: 为什么需要标记类为 `partial`
**A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。生成器会在编译时创建另一个部分类文件,包含生成的代码。
```csharp
[Log]
public partial class MyClass { } // ✅ 需要 partial
[Log]
public class MyClass { } // ❌ 编译错误,无法添加生成代码
```
### Q: 生成的代码在哪里?
**A**: 生成的代码在编译过程中创建,默认位置在 `obj/Debug/net6.0/generated/` 目录下。可以在项目文件中配置输出位置:
```xml
<PropertyGroup>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
```
### Q: 如何调试生成器问题?
**A**: 生成器提供了详细的诊断信息:
1. **查看错误列表**:编译错误会显示在 IDE 中
2. **查看生成文件**:检查生成的代码文件
3. **启用详细日志**:在项目文件中添加:
```xml
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
```
### Q: 可以自定义生成器吗?
**A**: 当前版本的生成器支持有限的配置。如需完全自定义,可以创建自己的源代码生成器项目。
### Q: 性能影响如何?
**A**: 源代码生成器对运行时性能的影响几乎为零:
- **编译时**:可能会增加编译时间(通常几秒)
- **运行时**:与手写代码性能相同
- **内存使用**:与手写代码内存使用相同
### Q: 与依赖注入框架兼容吗?
**A**: 完全兼容。生成器创建的是标准代码,可以与任何依赖注入框架配合使用:
```csharp
[Log]
[ContextAware]
public partial class ServiceComponent : IService
{
// 可以通过构造函数注入依赖
private readonly IDependency _dependency;
public ServiceComponent(IDependency dependency)
{
_dependency = dependency;
Logger.Info("Service initialized with dependency injection");
}
}
```
---
## 依赖关系
```mermaid
graph TD
A[GFramework.SourceGenerators] --> B[GFramework.SourceGenerators.Abstractions]
A --> C[GFramework.SourceGenerators.Common]
A --> D[GFramework.Core.Abstractions]
A --> E[Microsoft.CodeAnalysis.CSharp]
A --> F[Microsoft.CodeAnalysis.Analyzers]
```
## 版本兼容性
- **.NET**: 6.0+
- **Visual Studio**: 2022 17.0+
- **Rider**: 2022.3+
- **Roslyn**: 4.0+