mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 移除多余using语句和空行,统一代码缩进格式 - 优化注释文档中的缩进和对齐格式 - 简化条件语句和方法实现,提升代码可读性 - 重构协程系统相关类的字段和方法定义格式 - 更新架构服务中容器访问方式的实现 - 调整异步操作类的属性和方法组织结构 - [skip ci]
997 lines
24 KiB
Markdown
997 lines
24 KiB
Markdown
# 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+
|
||
|
||
## 许可证
|
||
|
||
本项目基于 Apache 2.0 许可证 - 详情请参阅 [LICENSE](../LICENSE) 文件。
|
||
|
||
---
|
||
|
||
**版本**: 1.0.0
|
||
**更新日期**: 2026-01-12 |