docs(godot): add architecture integration and design pattern docs

- Add documentation for Godot architecture integration, including AbstractArchitecture,
  ArchitectureAnchor, and IGodotModule
- Describe basic usage: architecture creation, initialization, and anchor usage
- Provide advanced examples: module system, lifecycle hooks, and hot-reload support
- Document ContextAware usage for accessing architecture within nodes
- Include best practices for multi-architecture setups and common pitfalls
- Add architecture design patterns guide (MVC, MVVM, Command, etc.)
- Cover event-driven, DI, and service locator patterns with examples

Note:
- Normalize line endings to LF to fix inconsistent diffs caused by CRLF/LF mismatch
- No functional code changes
This commit is contained in:
GeWuYou 2026-04-06 10:12:59 +08:00
parent c62893d0c2
commit aab0995f49
29 changed files with 19734 additions and 19734 deletions

View File

@ -1,57 +1,57 @@
using System; using System;
using System.Text; using System.Text;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
namespace GFramework.Core.Logging.Formatters; namespace GFramework.Core.Logging.Formatters;
/// <summary> /// <summary>
/// 默认日志格式化器,保持与现有格式兼容 /// 默认日志格式化器,保持与现有格式兼容
/// </summary> /// </summary>
public sealed class DefaultLogFormatter : ILogFormatter public sealed class DefaultLogFormatter : ILogFormatter
{ {
private static readonly string[] LevelStrings = private static readonly string[] LevelStrings =
[ [
"TRACE ", "TRACE ",
"DEBUG ", "DEBUG ",
"INFO ", "INFO ",
"WARNING", "WARNING",
"ERROR ", "ERROR ",
"FATAL " "FATAL "
]; ];
/// <summary> /// <summary>
/// 将日志条目格式化为默认格式 /// 将日志条目格式化为默认格式
/// </summary> /// </summary>
/// <param name="entry">日志条目</param> /// <param name="entry">日志条目</param>
/// <returns>格式化后的日志字符串</returns> /// <returns>格式化后的日志字符串</returns>
public string Format(LogEntry entry) public string Format(LogEntry entry)
{ {
var timestamp = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"); var timestamp = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff");
var levelStr = LevelStrings[(int)entry.Level]; var levelStr = LevelStrings[(int)entry.Level];
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append('[').Append(timestamp).Append("] ") sb.Append('[').Append(timestamp).Append("] ")
.Append(levelStr).Append(" [") .Append(levelStr).Append(" [")
.Append(entry.LoggerName).Append("] ") .Append(entry.LoggerName).Append("] ")
.Append(entry.Message); .Append(entry.Message);
// 添加结构化属性 // 添加结构化属性
var properties = entry.GetAllProperties(); var properties = entry.GetAllProperties();
if (properties.Count > 0) if (properties.Count > 0)
{ {
sb.Append(" |"); sb.Append(" |");
foreach (var prop in properties) foreach (var prop in properties)
{ {
sb.Append(' ').Append(prop.Key).Append('=').Append(prop.Value); sb.Append(' ').Append(prop.Key).Append('=').Append(prop.Value);
} }
} }
// 添加异常信息 // 添加异常信息
if (entry.Exception != null) if (entry.Exception != null)
{ {
sb.Append(global::System.Environment.NewLine).Append(entry.Exception); sb.Append(global::System.Environment.NewLine).Append(entry.Exception);
} }
return sb.ToString(); return sb.ToString();
} }
} }

View File

@ -1,177 +1,177 @@
# GFramework.Ecs.Arch - C# 标准集成方式 # GFramework.Ecs.Arch - C# 标准集成方式
## 为什么不使用 ModuleInitializer ## 为什么不使用 ModuleInitializer
`ModuleInitializer` 在 C# 中主要用于: `ModuleInitializer` 在 C# 中主要用于:
1. **应用程序代码** - 初始化应用程序级别的状态 1. **应用程序代码** - 初始化应用程序级别的状态
2. **高级源生成器** - 编译时代码生成 2. **高级源生成器** - 编译时代码生成
对于**库Library**来说,使用 `ModuleInitializer` 有以下问题: 对于**库Library**来说,使用 `ModuleInitializer` 有以下问题:
- ❌ 不符合 .NET 生态习惯 - ❌ 不符合 .NET 生态习惯
- ❌ 缺乏显式控制 - ❌ 缺乏显式控制
- ❌ 难以测试和调试 - ❌ 难以测试和调试
- ❌ 可能导致意外的副作用 - ❌ 可能导致意外的副作用
- ❌ 违反"显式优于隐式"原则 - ❌ 违反"显式优于隐式"原则
## C# 中的标准做法 ## C# 中的标准做法
### 1. 依赖注入扩展方法(推荐) ### 1. 依赖注入扩展方法(推荐)
这是 .NET 生态中最标准的做法,类似于: 这是 .NET 生态中最标准的做法,类似于:
- ASP.NET Core: `services.AddMvc()` - ASP.NET Core: `services.AddMvc()`
- Entity Framework: `services.AddDbContext()` - Entity Framework: `services.AddDbContext()`
- SignalR: `services.AddSignalR()` - SignalR: `services.AddSignalR()`
**优点:** **优点:**
- ✅ 符合 .NET 生态习惯 - ✅ 符合 .NET 生态习惯
- ✅ 显式、可控 - ✅ 显式、可控
- ✅ 易于测试 - ✅ 易于测试
- ✅ 支持配置 - ✅ 支持配置
- ✅ 支持链式调用 - ✅ 支持链式调用
### 2. 使用方式 ### 2. 使用方式
#### 基本用法 #### 基本用法
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnConfigure() protected override void OnConfigure()
{ {
// 显式添加 Arch ECS 支持 // 显式添加 Arch ECS 支持
Services.AddArch(); Services.AddArch();
} }
} }
``` ```
#### 带配置的用法 #### 带配置的用法
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnConfigure() protected override void OnConfigure()
{ {
Services.AddArch(options => Services.AddArch(options =>
{ {
options.WorldCapacity = 2000; options.WorldCapacity = 2000;
options.EnableStatistics = true; options.EnableStatistics = true;
options.Priority = 50; options.Priority = 50;
}); });
} }
} }
``` ```
#### 容器级别的用法 #### 容器级别的用法
```csharp ```csharp
var container = new MicrosoftDiContainer(); var container = new MicrosoftDiContainer();
container.AddArch(options => container.AddArch(options =>
{ {
options.WorldCapacity = 1000; options.WorldCapacity = 1000;
}); });
``` ```
### 3. 对比其他方案 ### 3. 对比其他方案
#### Spring Boot StarterJava #### Spring Boot StarterJava
```java ```java
// Spring Boot 使用自动配置 // Spring Boot 使用自动配置
@SpringBootApplication @SpringBootApplication
public class Application { public class Application {
// 自动扫描并加载 starter // 自动扫描并加载 starter
} }
``` ```
#### ASP.NET CoreC# #### ASP.NET CoreC#
```csharp ```csharp
// .NET 使用显式注册 // .NET 使用显式注册
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddSwagger(); builder.Services.AddSwagger();
``` ```
**C# 更倾向于显式注册**,因为: **C# 更倾向于显式注册**,因为:
1. 更清晰的依赖关系 1. 更清晰的依赖关系
2. 更好的 IDE 支持 2. 更好的 IDE 支持
3. 更容易调试和测试 3. 更容易调试和测试
4. 避免"魔法"行为 4. 避免"魔法"行为
### 4. 其他常见模式 ### 4. 其他常见模式
#### 4.1 Builder 模式 #### 4.1 Builder 模式
```csharp ```csharp
var architecture = new ArchitectureBuilder() var architecture = new ArchitectureBuilder()
.AddArch() .AddArch()
.AddLogging() .AddLogging()
.Build(); .Build();
``` ```
#### 4.2 Options 模式 #### 4.2 Options 模式
```csharp ```csharp
services.Configure<ArchOptions>(options => services.Configure<ArchOptions>(options =>
{ {
options.WorldCapacity = 2000; options.WorldCapacity = 2000;
}); });
``` ```
#### 4.3 静态工厂方法 #### 4.3 静态工厂方法
```csharp ```csharp
var module = ArchEcsModule.Create(options => var module = ArchEcsModule.Create(options =>
{ {
options.WorldCapacity = 1000; options.WorldCapacity = 1000;
}); });
``` ```
## 迁移指南 ## 迁移指南
### 从 ModuleInitializer 迁移 ### 从 ModuleInitializer 迁移
**之前(自动注册):** **之前(自动注册):**
```csharp ```csharp
// 只需引入包,自动注册 // 只需引入包,自动注册
// 无需任何代码 // 无需任何代码
``` ```
**现在(显式注册):** **现在(显式注册):**
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnConfigure() protected override void OnConfigure()
{ {
Services.AddArch(); Services.AddArch();
} }
} }
``` ```
### 优势 ### 优势
1. **更清晰** - 一眼就能看出使用了哪些模块 1. **更清晰** - 一眼就能看出使用了哪些模块
2. **更可控** - 可以决定何时、如何注册 2. **更可控** - 可以决定何时、如何注册
3. **更灵活** - 可以传递配置参数 3. **更灵活** - 可以传递配置参数
4. **更标准** - 符合 .NET 生态习惯 4. **更标准** - 符合 .NET 生态习惯
## 总结 ## 总结
C# 生态更倾向于**显式优于隐式**的设计哲学,因此: C# 生态更倾向于**显式优于隐式**的设计哲学,因此:
- ✅ **推荐**:使用扩展方法 `AddArch()` - ✅ **推荐**:使用扩展方法 `AddArch()`
- ❌ **不推荐**:使用 `ModuleInitializer` - ❌ **不推荐**:使用 `ModuleInitializer`
这样的设计: 这样的设计:
1. 符合 .NET 生态习惯 1. 符合 .NET 生态习惯
2. 提供更好的开发体验 2. 提供更好的开发体验
3. 更容易理解和维护 3. 更容易理解和维护
4. 避免 CA2255 警告 4. 避免 CA2255 警告

View File

@ -1,314 +1,314 @@
# GFramework.Ecs.Arch - 从自动注册到显式注册的迁移 # GFramework.Ecs.Arch - 从自动注册到显式注册的迁移
## 变更原因 ## 变更原因
### 问题CA2255 警告 ### 问题CA2255 警告
``` ```
warning CA2255: The 'ModuleInitializer' attribute is only intended to be used warning CA2255: The 'ModuleInitializer' attribute is only intended to be used
in application code or advanced source generator scenarios in application code or advanced source generator scenarios
``` ```
### 为什么 ModuleInitializer 不适合库? ### 为什么 ModuleInitializer 不适合库?
在 C# 生态中,`ModuleInitializer` 主要用于: 在 C# 生态中,`ModuleInitializer` 主要用于:
1. **应用程序代码** - 初始化应用程序级别的状态 1. **应用程序代码** - 初始化应用程序级别的状态
2. **高级源生成器** - 编译时代码生成 2. **高级源生成器** - 编译时代码生成
对于**库Library**来说,使用 `ModuleInitializer` 有以下问题: 对于**库Library**来说,使用 `ModuleInitializer` 有以下问题:
- ❌ 不符合 .NET 生态习惯 - ❌ 不符合 .NET 生态习惯
- ❌ 缺乏显式控制 - ❌ 缺乏显式控制
- ❌ 难以测试和调试 - ❌ 难以测试和调试
- ❌ 可能导致意外的副作用 - ❌ 可能导致意外的副作用
- ❌ 违反"显式优于隐式"原则 - ❌ 违反"显式优于隐式"原则
- ❌ 触发 CA2255 警告 - ❌ 触发 CA2255 警告
## C# 标准做法对比 ## C# 标准做法对比
### Spring BootJava- 自动配置 ### Spring BootJava- 自动配置
```java ```java
@SpringBootApplication @SpringBootApplication
public class Application { public class Application {
// 自动扫描并加载 starter // 自动扫描并加载 starter
} }
``` ```
### ASP.NET CoreC#- 显式注册 ### ASP.NET CoreC#- 显式注册
```csharp ```csharp
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // 显式注册 builder.Services.AddControllers(); // 显式注册
builder.Services.AddSwagger(); // 显式注册 builder.Services.AddSwagger(); // 显式注册
``` ```
**C# 更倾向于显式注册**,因为: **C# 更倾向于显式注册**,因为:
1. ✅ 更清晰的依赖关系 1. ✅ 更清晰的依赖关系
2. ✅ 更好的 IDE 支持 2. ✅ 更好的 IDE 支持
3. ✅ 更容易调试和测试 3. ✅ 更容易调试和测试
4. ✅ 避免"魔法"行为 4. ✅ 避免"魔法"行为
## 变更内容 ## 变更内容
### 1. 移除 ModuleInitializer ### 1. 移除 ModuleInitializer
**之前:** **之前:**
```csharp ```csharp
// ArchModuleInitializer.cs // ArchModuleInitializer.cs
[ModuleInitializer] [ModuleInitializer]
public static void Initialize() public static void Initialize()
{ {
ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true)); ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true));
} }
``` ```
**现在:** **现在:**
```csharp ```csharp
// 文件已删除 // 文件已删除
``` ```
### 2. 新增扩展方法 ### 2. 新增扩展方法
**新增:** **新增:**
```csharp ```csharp
// ArchExtensions.cs // ArchExtensions.cs
public static class ArchExtensions public static class ArchExtensions
{ {
/// <summary> /// <summary>
/// 添加 Arch ECS 支持到架构服务中 /// 添加 Arch ECS 支持到架构服务中
/// </summary> /// </summary>
public static IArchitectureServices AddArch( public static IArchitectureServices AddArch(
this IArchitectureServices services, this IArchitectureServices services,
Action<ArchOptions>? configure = null) Action<ArchOptions>? configure = null)
{ {
var options = new ArchOptions(); var options = new ArchOptions();
configure?.Invoke(options); configure?.Invoke(options);
ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true)); ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true));
return services; return services;
} }
/// <summary> /// <summary>
/// 添加 Arch ECS 支持到 IoC 容器中 /// 添加 Arch ECS 支持到 IoC 容器中
/// </summary> /// </summary>
public static IIocContainer AddArch( public static IIocContainer AddArch(
this IIocContainer container, this IIocContainer container,
Action<ArchOptions>? configure = null) Action<ArchOptions>? configure = null)
{ {
var options = new ArchOptions(); var options = new ArchOptions();
configure?.Invoke(options); configure?.Invoke(options);
ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true)); ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true));
return container; return container;
} }
} }
``` ```
### 3. 更新使用方式 ### 3. 更新使用方式
#### 之前(自动注册) #### 之前(自动注册)
```csharp ```csharp
// 只需引入包,自动注册 // 只需引入包,自动注册
// 无需任何代码 // 无需任何代码
``` ```
#### 现在(显式注册) #### 现在(显式注册)
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnConfigure() protected override void OnConfigure()
{ {
// 显式注册 // 显式注册
Services.AddArch(); Services.AddArch();
} }
} }
``` ```
#### 带配置 #### 带配置
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnConfigure() protected override void OnConfigure()
{ {
Services.AddArch(options => Services.AddArch(options =>
{ {
options.WorldCapacity = 2000; options.WorldCapacity = 2000;
options.EnableStatistics = true; options.EnableStatistics = true;
options.Priority = 50; options.Priority = 50;
}); });
} }
} }
``` ```
### 4. 更新测试 ### 4. 更新测试
**之前:** **之前:**
```csharp ```csharp
[Test] [Test]
public void ArchEcsModule_Should_Be_Auto_Registered() public void ArchEcsModule_Should_Be_Auto_Registered()
{ {
// 手动触发模块初始化器 // 手动触发模块初始化器
ArchModuleInitializer.Initialize(); ArchModuleInitializer.Initialize();
var services = new ArchitectureServices(); var services = new ArchitectureServices();
services.ModuleManager.RegisterBuiltInModules(...); services.ModuleManager.RegisterBuiltInModules(...);
// ... // ...
} }
``` ```
**现在:** **现在:**
```csharp ```csharp
[Test] [Test]
public void ArchEcsModule_Should_Be_Explicitly_Registered() public void ArchEcsModule_Should_Be_Explicitly_Registered()
{ {
var services = new ArchitectureServices(); var services = new ArchitectureServices();
// 显式注册 // 显式注册
services.AddArch(); services.AddArch();
services.ModuleManager.RegisterBuiltInModules(...); services.ModuleManager.RegisterBuiltInModules(...);
// ... // ...
} }
``` ```
## 优势对比 ## 优势对比
### 自动注册ModuleInitializer ### 自动注册ModuleInitializer
- ❌ 触发 CA2255 警告 - ❌ 触发 CA2255 警告
- ❌ 不符合 .NET 生态习惯 - ❌ 不符合 .NET 生态习惯
- ❌ 缺乏显式控制 - ❌ 缺乏显式控制
- ❌ 难以测试 - ❌ 难以测试
- ❌ "魔法"行为 - ❌ "魔法"行为
### 显式注册(扩展方法) ### 显式注册(扩展方法)
- ✅ 无警告 - ✅ 无警告
- ✅ 符合 .NET 生态习惯 - ✅ 符合 .NET 生态习惯
- ✅ 显式、可控 - ✅ 显式、可控
- ✅ 易于测试 - ✅ 易于测试
- ✅ 支持配置 - ✅ 支持配置
- ✅ 支持链式调用 - ✅ 支持链式调用
- ✅ 更好的 IDE 支持 - ✅ 更好的 IDE 支持
## 迁移指南 ## 迁移指南
### 对于现有用户 ### 对于现有用户
如果你之前使用自动注册方式,需要进行以下更改: 如果你之前使用自动注册方式,需要进行以下更改:
**步骤 1更新包** **步骤 1更新包**
```bash ```bash
dotnet update package GeWuYou.GFramework.Ecs.Arch dotnet update package GeWuYou.GFramework.Ecs.Arch
``` ```
**步骤 2添加显式注册** **步骤 2添加显式注册**
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnConfigure() protected override void OnConfigure()
{ {
// 添加这一行 // 添加这一行
Services.AddArch(); Services.AddArch();
} }
} }
``` ```
**步骤 3可选添加配置** **步骤 3可选添加配置**
```csharp ```csharp
Services.AddArch(options => Services.AddArch(options =>
{ {
options.WorldCapacity = 2000; options.WorldCapacity = 2000;
options.EnableStatistics = true; options.EnableStatistics = true;
}); });
``` ```
## 验证结果 ## 验证结果
### 构建验证 ✅ ### 构建验证 ✅
```bash ```bash
dotnet build GFramework.sln dotnet build GFramework.sln
# Build succeeded. 39 Warning(s), 0 Error(s) # Build succeeded. 39 Warning(s), 0 Error(s)
# 无 CA2255 警告 # 无 CA2255 警告
``` ```
### 测试验证 ✅ ### 测试验证 ✅
```bash ```bash
dotnet test --filter "ExplicitRegistrationTests" dotnet test --filter "ExplicitRegistrationTests"
# Pas 4, Failed: 0, Total: 4 # Pas 4, Failed: 0, Total: 4
``` ```
**测试用例:** **测试用例:**
1. ✅ `ArchEcsModule_Should_Be_Explicitly_Registered` - 验证显式注册 1. ✅ `ArchEcsModule_Should_Be_Explicitly_Registered` - 验证显式注册
2. ✅ `World_Should_Be_Registered_In_Container` - 验证 World 注册 2. ✅ `World_Should_Be_Registered_In_Container` - 验证 World 注册
3. ✅ `AddArch_Should_Accept_Configuration` - 验证配置支持 3. ✅ `AddArch_Should_Accept_Configuration` - 验证配置支持
4. ✅ `Container_AddArch_Should_Work` - 验证容器级别注册 4. ✅ `Container_AddArch_Should_Work` - 验证容器级别注册
## 参考资料 ## 参考资料
### .NET 生态中的类似实现 ### .NET 生态中的类似实现
1. **ASP.NET Core** 1. **ASP.NET Core**
```csharp ```csharp
services.AddMvc(); services.AddMvc();
services.AddControllers(); services.AddControllers();
``` ```
2. **Entity Framework Core** 2. **Entity Framework Core**
```csharp ```csharp
services.AddDbContext<MyContext>(); services.AddDbContext<MyContext>();
``` ```
3. **SignalR** 3. **SignalR**
```csharp ```csharp
services.AddSignalR(); services.AddSignalR();
``` ```
4. **Swagger** 4. **Swagger**
```csharp ```csharp
services.AddSwaggerGen(); services.AddSwaggerGen();
``` ```
### 相关文档 ### 相关文档
-N_PATTERN.md](INTEGRATION_PATTERN.md) - 集成模式详解 -N_PATTERN.md](INTEGRATION_PATTERN.md) - 集成模式详解
- [README.md](README.md) - 使用指南 - [README.md](README.md) - 使用指南
- [CA2255 规则说明](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2255) - [CA2255 规则说明](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2255)
## 总结 ## 总结
通过从 `ModuleInitializer` 迁移到显式注册扩展方法: 通过从 `ModuleInitializer` 迁移到显式注册扩展方法:
1. ✅ **消除警告** - 不再触发 CA2255 1. ✅ **消除警告** - 不再触发 CA2255
2. ✅ **符合习惯** - 遵循 .NET 生态标准 2. ✅ **符合习惯** - 遵循 .NET 生态标准
3. ✅ **更好控制** - 显式、可配置 3. ✅ **更好控制** - 显式、可配置
4. ✅ **易于测试** - 清晰的测试边界 4. ✅ **易于测试** - 清晰的测试边界
5. ✅ **更好体验** - IDE 支持、链式调用 5. ✅ **更好体验** - IDE 支持、链式调用
这是一个**破坏性变更**,但带来了更好的开发体验和更符合 .NET 生态的设计。 这是一个**破坏性变更**,但带来了更好的开发体验和更符合 .NET 生态的设计。
--- ---
**变更日期:** 2026-03-08 **变更日期:** 2026-03-08
**影响范围:** 所有使用 GFramework.Ecs.Arch 的项目 **影响范围:** 所有使用 GFramework.Ecs.Arch 的项目
**迁移难度:** 低(只需添加一行代码) **迁移难度:** 低(只需添加一行代码)

View File

@ -1,20 +1,20 @@
// IsExternalInit.cs // IsExternalInit.cs
// This type is required to support init-only setters and record types // This type is required to support init-only setters and record types
// when targeting netstandard2.0 or older frameworks. // when targeting netstandard2.0 or older frameworks.
#if !NET5_0_OR_GREATER #if !NET5_0_OR_GREATER
using System.ComponentModel; using System.ComponentModel;
// ReSharper disable CheckNamespace // ReSharper disable CheckNamespace
namespace System.Runtime.CompilerServices; namespace System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// 提供一个占位符类型,用于支持 C# 9.0 的 init 访问器功能。 /// 提供一个占位符类型,用于支持 C# 9.0 的 init 访问器功能。
/// 该类型在 .NET 5.0 及更高版本中已内置,因此仅在较低版本的 .NET 中定义。 /// 该类型在 .NET 5.0 及更高版本中已内置,因此仅在较低版本的 .NET 中定义。
/// </summary> /// </summary>
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit internal static class IsExternalInit
{ {
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,491 +1,491 @@
# 最佳实践 # 最佳实践
本文档总结了使用 GFramework 的最佳实践和设计模式。 本文档总结了使用 GFramework 的最佳实践和设计模式。
## 架构设计 ## 架构设计
### 1. 清晰的职责分离 ### 1. 清晰的职责分离
**原则**:每一层都有明确的职责,不要混淆。 **原则**:每一层都有明确的职责,不要混淆。
```csharp ```csharp
// ✅ 正确的职责分离 // ✅ 正确的职责分离
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// Model只存储数据 // Model只存储数据
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Score { get; } = new(0); public BindableProperty<int> Score { get; } = new(0);
} }
public class CombatSystem : AbstractSystem public class CombatSystem : AbstractSystem
{ {
// System处理业务逻辑 // System处理业务逻辑
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<AttackEvent>(OnAttack); this.RegisterEvent<AttackEvent>(OnAttack);
} }
private void OnAttack(AttackEvent e) private void OnAttack(AttackEvent e)
{ {
var player = this.GetModel<PlayerModel>(); var player = this.GetModel<PlayerModel>();
player.Health.Value -= e.Damage; player.Health.Value -= e.Damage;
} }
} }
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class PlayerController : IController public partial class PlayerController : IController
{ {
// Controller连接 UI 和逻辑 // Controller连接 UI 和逻辑
public void Initialize() public void Initialize()
{ {
var player = this.GetModel<PlayerModel>(); var player = this.GetModel<PlayerModel>();
player.Health.RegisterWithInitValue(OnHealthChanged); player.Health.RegisterWithInitValue(OnHealthChanged);
} }
private void OnHealthChanged(int health) private void OnHealthChanged(int health)
{ {
UpdateHealthDisplay(health); UpdateHealthDisplay(health);
} }
} }
``` ```
### 2. 事件驱动设计 ### 2. 事件驱动设计
**原则**:使用事件解耦组件,避免直接调用。 **原则**:使用事件解耦组件,避免直接调用。
```csharp ```csharp
// ❌ 紧耦合 // ❌ 紧耦合
public class SystemA : AbstractSystem public class SystemA : AbstractSystem
{ {
private void OnEvent(EventA e) private void OnEvent(EventA e)
{ {
var systemB = this.GetSystem<SystemB>(); var systemB = this.GetSystem<SystemB>();
systemB.DoSomething(); // 直接调用 systemB.DoSomething(); // 直接调用
} }
} }
// ✅ 松耦合 // ✅ 松耦合
public class SystemA : AbstractSystem public class SystemA : AbstractSystem
{ {
private void OnEvent(EventA e) private void OnEvent(EventA e)
{ {
this.SendEvent(new EventB()); // 发送事件 this.SendEvent(new EventB()); // 发送事件
} }
} }
public class SystemB : AbstractSystem public class SystemB : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<EventB>(OnEventB); this.RegisterEvent<EventB>(OnEventB);
} }
} }
``` ```
### 3. 命令查询分离 ### 3. 命令查询分离
**原则**明确区分修改状态Command和查询状态Query **原则**明确区分修改状态Command和查询状态Query
```csharp ```csharp
// ✅ 正确的 CQRS // ✅ 正确的 CQRS
public class MovePlayerCommand : AbstractCommand public class MovePlayerCommand : AbstractCommand
{ {
public Vector2 Direction { get; set; } public Vector2 Direction { get; set; }
protected override void OnDo() protected override void OnDo()
{ {
// 修改状态 // 修改状态
this.SendEvent(new PlayerMovedEvent { Direction = Direction }); this.SendEvent(new PlayerMovedEvent { Direction = Direction });
} }
} }
public class GetPlayerPositionQuery : AbstractQuery<Vector2> public class GetPlayerPositionQuery : AbstractQuery<Vector2>
{ {
protected override Vector2 OnDo() protected override Vector2 OnDo()
{ {
// 只查询,不修改 // 只查询,不修改
return this.GetModel<PlayerModel>().Position.Value; return this.GetModel<PlayerModel>().Position.Value;
} }
} }
``` ```
## 代码组织 ## 代码组织
### 1. 项目结构 ### 1. 项目结构
``` ```
GameProject/ GameProject/
├── Models/ ├── Models/
│ ├── PlayerModel.cs │ ├── PlayerModel.cs
│ ├── GameStateModel.cs │ ├── GameStateModel.cs
│ └── InventoryModel.cs │ └── InventoryModel.cs
├── Systems/ ├── Systems/
│ ├── CombatSystem.cs │ ├── CombatSystem.cs
│ ├── InventorySystem.cs │ ├── InventorySystem.cs
│ └── GameLogicSystem.cs │ └── GameLogicSystem.cs
├── Commands/ ├── Commands/
│ ├── AttackCommand.cs │ ├── AttackCommand.cs
│ ├── MoveCommand.cs │ ├── MoveCommand.cs
│ └── UseItemCommand.cs │ └── UseItemCommand.cs
├── Queries/ ├── Queries/
│ ├── GetPlayerHealthQuery.cs │ ├── GetPlayerHealthQuery.cs
│ └── GetInventoryItemsQuery.cs │ └── GetInventoryItemsQuery.cs
├── Events/ ├── Events/
│ ├── PlayerDiedEvent.cs │ ├── PlayerDiedEvent.cs
│ ├── ItemUsedEvent.cs │ ├── ItemUsedEvent.cs
│ └── EnemyDamagedEvent.cs │ └── EnemyDamagedEvent.cs
├── Controllers/ ├── Controllers/
│ ├── PlayerController.cs │ ├── PlayerController.cs
│ └── UIController.cs │ └── UIController.cs
├── Utilities/ ├── Utilities/
│ ├── StorageUtility.cs │ ├── StorageUtility.cs
│ └── MathUtility.cs │ └── MathUtility.cs
└── GameArchitecture.cs └── GameArchitecture.cs
``` ```
### 2. 命名规范 ### 2. 命名规范
```csharp ```csharp
// Models使用 Model 后缀 // Models使用 Model 后缀
public class PlayerModel : AbstractModel { } public class PlayerModel : AbstractModel { }
public class GameStateModel : AbstractModel { } public class GameStateModel : AbstractModel { }
// Systems使用 System 后缀 // Systems使用 System 后缀
public class CombatSystem : AbstractSystem { } public class CombatSystem : AbstractSystem { }
public class InventorySystem : AbstractSystem { } public class InventorySystem : AbstractSystem { }
// Commands使用 Command 后缀 // Commands使用 Command 后缀
public class AttackCommand : AbstractCommand { } public class AttackCommand : AbstractCommand { }
public class MoveCommand : AbstractCommand { } public class MoveCommand : AbstractCommand { }
// Queries使用 Query 后缀 // Queries使用 Query 后缀
public class GetPlayerHealthQuery : AbstractQuery<int> { } public class GetPlayerHealthQuery : AbstractQuery<int> { }
public class GetInventoryItemsQuery : AbstractQuery<List<Item>> { } public class GetInventoryItemsQuery : AbstractQuery<List<Item>> { }
// Events使用 Event 后缀 // Events使用 Event 后缀
public class PlayerDiedEvent : IEvent { } public class PlayerDiedEvent : IEvent { }
public class ItemUsedEvent : IEvent { } public class ItemUsedEvent : IEvent { }
// Controllers使用 Controller 后缀 // Controllers使用 Controller 后缀
public class PlayerController : IController { } public class PlayerController : IController { }
// Utilities使用 Utility 后缀 // Utilities使用 Utility 后缀
public class StorageUtility : IUtility { } public class StorageUtility : IUtility { }
``` ```
## 内存管理 ## 内存管理
### 1. 正确的注销管理 ### 1. 正确的注销管理
```csharp ```csharp
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class MyController : IController public partial class MyController : IController
{ {
private IUnRegisterList _unregisterList = new UnRegisterList(); private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize() public void Initialize()
{ {
var model = this.GetModel<PlayerModel>(); var model = this.GetModel<PlayerModel>();
// 注册事件并添加到注销列表 // 注册事件并添加到注销列表
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied) this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
// 注册属性监听并添加到注销列表 // 注册属性监听并添加到注销列表
model.Health.Register(OnHealthChanged) model.Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
} }
public void Cleanup() public void Cleanup()
{ {
// 统一注销所有监听器 // 统一注销所有监听器
_unregisterList.UnRegisterAll(); _unregisterList.UnRegisterAll();
} }
private void OnPlayerDied(PlayerDiedEvent e) { } private void OnPlayerDied(PlayerDiedEvent e) { }
private void OnHealthChanged(int health) { } private void OnHealthChanged(int health) { }
} }
``` ```
### 2. 生命周期管理 ### 2. 生命周期管理
```csharp ```csharp
public class GameManager public class GameManager
{ {
private GameArchitecture _architecture; private GameArchitecture _architecture;
public void StartGame() public void StartGame()
{ {
_architecture = new GameArchitecture(); _architecture = new GameArchitecture();
_architecture.Initialize(); _architecture.Initialize();
} }
public void EndGame() public void EndGame()
{ {
// 销毁架构,自动清理所有组件 // 销毁架构,自动清理所有组件
_architecture.Destroy(); _architecture.Destroy();
_architecture = null; _architecture = null;
} }
} }
``` ```
## 性能优化 ## 性能优化
### 1. 缓存组件引用 ### 1. 缓存组件引用
```csharp ```csharp
// ❌ 低效:每次都查询 // ❌ 低效:每次都查询
public void Update() public void Update()
{ {
var model = this.GetModel<PlayerModel>(); var model = this.GetModel<PlayerModel>();
model.Health.Value -= 1; model.Health.Value -= 1;
} }
// ✅ 高效:缓存引用 // ✅ 高效:缓存引用
private PlayerModel _playerModel; private PlayerModel _playerModel;
public void Initialize() public void Initialize()
{ {
_playerModel = this.GetModel<PlayerModel>(); _playerModel = this.GetModel<PlayerModel>();
} }
public void Update() public void Update()
{ {
_playerModel.Health.Value -= 1; _playerModel.Health.Value -= 1;
} }
``` ```
### 2. 避免频繁的事件创建 ### 2. 避免频繁的事件创建
```csharp ```csharp
// ❌ 低效:每帧创建新事件 // ❌ 低效:每帧创建新事件
public void Update() public void Update()
{ {
this.SendEvent(new UpdateEvent()); // 频繁分配内存 this.SendEvent(new UpdateEvent()); // 频繁分配内存
} }
// ✅ 高效:复用事件或使用对象池 // ✅ 高效:复用事件或使用对象池
private UpdateEvent _updateEvent = new UpdateEvent(); private UpdateEvent _updateEvent = new UpdateEvent();
public void Update() public void Update()
{ {
this.SendEvent(_updateEvent); this.SendEvent(_updateEvent);
} }
``` ```
### 3. 异步处理重操作 ### 3. 异步处理重操作
```csharp ```csharp
public class LoadDataCommand : AbstractCommand public class LoadDataCommand : AbstractCommand
{ {
protected override async void OnDo() protected override async void OnDo()
{ {
// 异步加载数据,不阻塞主线程 // 异步加载数据,不阻塞主线程
var data = await LoadDataAsync(); var data = await LoadDataAsync();
this.SendEvent(new DataLoadedEvent { Data = data }); this.SendEvent(new DataLoadedEvent { Data = data });
} }
private async Task<Data> LoadDataAsync() private async Task<Data> LoadDataAsync()
{ {
return await Task.Run(() => return await Task.Run(() =>
{ {
// 耗时操作 // 耗时操作
return new Data(); return new Data();
}); });
} }
} }
``` ```
## 测试 ## 测试
### 1. 单元测试 ### 1. 单元测试
```csharp ```csharp
[TestFixture] [TestFixture]
public class CombatSystemTests public class CombatSystemTests
{ {
private GameArchitecture _architecture; private GameArchitecture _architecture;
private PlayerModel _playerModel; private PlayerModel _playerModel;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_architecture = new TestArchitecture(); _architecture = new TestArchitecture();
_architecture.Initialize(); _architecture.Initialize();
_playerModel = _architecture.GetModel<PlayerModel>(); _playerModel = _architecture.GetModel<PlayerModel>();
} }
[TearDown] [TearDown]
public void Teardown() public void Teardown()
{ {
_architecture.Destroy(); _architecture.Destroy();
} }
[Test] [Test]
public void PlayerTakeDamage_ReducesHealth() public void PlayerTakeDamage_ReducesHealth()
{ {
_playerModel.Health.Value = 100; _playerModel.Health.Value = 100;
_architecture.SendEvent(new DamageEvent { Amount = 10 }); _architecture.SendEvent(new DamageEvent { Amount = 10 });
Assert.AreEqual(90, _playerModel.Health.Value); Assert.AreEqual(90, _playerModel.Health.Value);
} }
[Test] [Test]
public void PlayerDies_WhenHealthReachesZero() public void PlayerDies_WhenHealthReachesZero()
{ {
_playerModel.Health.Value = 10; _playerModel.Health.Value = 10;
_architecture.SendEvent(new DamageEvent { Amount = 10 }); _architecture.SendEvent(new DamageEvent { Amount = 10 });
Assert.AreEqual(0, _playerModel.Health.Value); Assert.AreEqual(0, _playerModel.Health.Value);
} }
} }
``` ```
### 2. 集成测试 ### 2. 集成测试
```csharp ```csharp
[TestFixture] [TestFixture]
public class GameFlowTests public class GameFlowTests
{ {
private GameArchitecture _architecture; private GameArchitecture _architecture;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_architecture = new GameArchitecture(); _architecture = new GameArchitecture();
_architecture.Initialize(); _architecture.Initialize();
} }
[Test] [Test]
public void CompleteGameFlow() public void CompleteGameFlow()
{ {
// 初始化 // 初始化
var player = _architecture.GetModel<PlayerModel>(); var player = _architecture.GetModel<PlayerModel>();
Assert.AreEqual(100, player.Health.Value); Assert.AreEqual(100, player.Health.Value);
// 执行操作 // 执行操作
_architecture.SendCommand(new AttackCommand { Damage = 20 }); _architecture.SendCommand(new AttackCommand { Damage = 20 });
// 验证结果 // 验证结果
Assert.AreEqual(80, player.Health.Value); Assert.AreEqual(80, player.Health.Value);
} }
} }
``` ```
## 文档 ## 文档
### 1. 代码注释 ### 1. 代码注释
```csharp ```csharp
/// <summary> /// <summary>
/// 玩家模型,存储玩家的所有状态数据 /// 玩家模型,存储玩家的所有状态数据
/// </summary> /// </summary>
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
/// <summary> /// <summary>
/// 玩家的生命值,使用 BindableProperty 实现响应式更新 /// 玩家的生命值,使用 BindableProperty 实现响应式更新
/// </summary> /// </summary>
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
protected override void OnInit() protected override void OnInit()
{ {
// 监听生命值变化,当生命值为 0 时发送死亡事件 // 监听生命值变化,当生命值为 0 时发送死亡事件
Health.Register(hp => Health.Register(hp =>
{ {
if (hp <= 0) if (hp <= 0)
this.SendEvent(new PlayerDiedEvent()); this.SendEvent(new PlayerDiedEvent());
}); });
} }
} }
``` ```
### 2. 架构文档 ### 2. 架构文档
为你的项目编写架构文档,说明: 为你的项目编写架构文档,说明:
- 主要的 Model、System、Command、Query - 主要的 Model、System、Command、Query
- 关键事件流 - 关键事件流
- 组件间的通信方式 - 组件间的通信方式
- 扩展点和插件机制 - 扩展点和插件机制
## 常见陷阱 ## 常见陷阱
### 1. 在 Model 中包含业务逻辑 ### 1. 在 Model 中包含业务逻辑
```csharp ```csharp
// ❌ 错误 // ❌ 错误
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
public void TakeDamage(int damage) public void TakeDamage(int damage)
{ {
Health.Value -= damage; Health.Value -= damage;
if (Health.Value <= 0) if (Health.Value <= 0)
Die(); Die();
} }
} }
// ✅ 正确 // ✅ 正确
public class CombatSystem : AbstractSystem public class CombatSystem : AbstractSystem
{ {
private void OnDamage(DamageEvent e) private void OnDamage(DamageEvent e)
{ {
var player = this.GetModel<PlayerModel>(); var player = this.GetModel<PlayerModel>();
player.Health.Value -= e.Amount; player.Health.Value -= e.Amount;
} }
} }
``` ```
### 2. 忘记注销监听器 ### 2. 忘记注销监听器
```csharp ```csharp
// ❌ 错误:可能导致内存泄漏 // ❌ 错误:可能导致内存泄漏
public void Initialize() public void Initialize()
{ {
this.RegisterEvent<Event1>(OnEvent1); // 未注销 this.RegisterEvent<Event1>(OnEvent1); // 未注销
} }
// ✅ 正确 // ✅ 正确
private IUnRegisterList _unregisterList = new UnRegisterList(); private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize() public void Initialize()
{ {
this.RegisterEvent<Event1>(OnEvent1) this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
} }
public void Cleanup() public void Cleanup()
{ {
_unregisterList.UnRegisterAll(); _unregisterList.UnRegisterAll();
} }
``` ```
### 3. 直接调用其他系统 ### 3. 直接调用其他系统
```csharp ```csharp
// ❌ 错误:紧耦合 // ❌ 错误:紧耦合
public class SystemA : AbstractSystem public class SystemA : AbstractSystem
{ {
private void OnEvent(EventA e) private void OnEvent(EventA e)
{ {
var systemB = this.GetSystem<SystemB>(); var systemB = this.GetSystem<SystemB>();
systemB.DoSomething(); systemB.DoSomething();
} }
} }
// ✅ 正确:使用事件解耦 // ✅ 正确:使用事件解耦
public class SystemA : AbstractSystem public class SystemA : AbstractSystem
{ {
private void OnEvent(EventA e) private void OnEvent(EventA e)
{ {
this.SendEvent(new EventB()); this.SendEvent(new EventB());
} }
} }
``` ```
--- ---
遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。 遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,19 @@
# Architecture 架构详解 # Architecture 架构详解
> 深入了解 GFramework 的核心架构设计和实现 > 深入了解 GFramework 的核心架构设计和实现
## 目录 ## 目录
- [概述](#概述) - [概述](#概述)
- [架构设计](#架构设计) - [架构设计](#架构设计)
- [生命周期管理](#生命周期管理) - [生命周期管理](#生命周期管理)
- [组件注册](#组件注册) - [组件注册](#组件注册)
- [模块系统](#模块系统) - [模块系统](#模块系统)
- [最佳实践](#最佳实践) - [最佳实践](#最佳实践)
- [API 参考](#api-参考) - [API 参考](#api-参考)
## 概述 ## 概述
Architecture 是 GFramework 的核心类,负责管理整个应用的生命周期、组件注册和模块管理。从 v1.1.0 开始,Architecture Architecture 是 GFramework 的核心类,负责管理整个应用的生命周期、组件注册和模块管理。从 v1.1.0 开始,Architecture
采用模块化设计,将职责分离到专门的协作者中。 采用模块化设计,将职责分离到专门的协作者中。
@ -21,54 +21,54 @@ Architecture 是 GFramework 的核心类,负责管理整个应用的生命周期
> - `ArchitectureServices` 是公开的基础服务入口,负责容器、事件总线、命令执行器、查询执行器和服务模块管理 > - `ArchitectureServices` 是公开的基础服务入口,负责容器、事件总线、命令执行器、查询执行器和服务模块管理
> - `ArchitectureComponentRegistry` 是内部组件注册器,专门负责 System / Model / Utility 的注册与生命周期接入 > - `ArchitectureComponentRegistry` 是内部组件注册器,专门负责 System / Model / Utility 的注册与生命周期接入
> - 两者不是同一层职责,不要混用 > - 两者不是同一层职责,不要混用
### 设计目标 ### 设计目标
- **单一职责**: 每个管理器只负责一个明确的功能 - **单一职责**: 每个管理器只负责一个明确的功能
- **类型安全**: 基于泛型的组件获取和注册 - **类型安全**: 基于泛型的组件获取和注册
- **生命周期管理**: 自动的初始化和销毁机制 - **生命周期管理**: 自动的初始化和销毁机制
- **可扩展性**: 支持模块和钩子扩展 - **可扩展性**: 支持模块和钩子扩展
- **向后兼容**: 保持公共 API 稳定 - **向后兼容**: 保持公共 API 稳定
### 核心组件 ### 核心组件
``` ```
Architecture (核心协调器) Architecture (核心协调器)
├── ArchitectureBootstrapper (初始化基础设施编排) ├── ArchitectureBootstrapper (初始化基础设施编排)
├── ArchitectureLifecycle (生命周期管理) ├── ArchitectureLifecycle (生命周期管理)
├── ArchitectureComponentRegistry (组件注册) ├── ArchitectureComponentRegistry (组件注册)
└── ArchitectureModules (模块管理) └── ArchitectureModules (模块管理)
``` ```
## 架构设计 ## 架构设计
### 设计模式 ### 设计模式
Architecture 采用以下设计模式: Architecture 采用以下设计模式:
1. **组合模式 (Composition)**: Architecture 组合多个内部协作者 1. **组合模式 (Composition)**: Architecture 组合多个内部协作者
2. **委托模式 (Delegation)**: 方法调用委托给专门的管理器 2. **委托模式 (Delegation)**: 方法调用委托给专门的管理器
3. **协调器模式 (Coordinator)**: Architecture 作为协调器统一对外接口 3. **协调器模式 (Coordinator)**: Architecture 作为协调器统一对外接口
### 类图 ### 类图
``` ```
┌─────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────┐
│ Architecture │ │ Architecture │
│ - _bootstrapper: ArchitectureBootstrapper │ │ - _bootstrapper: ArchitectureBootstrapper │
│ - _lifecycle: ArchitectureLifecycle │ │ - _lifecycle: ArchitectureLifecycle │
│ - _componentRegistry: ArchitectureComponentRegistry│ │ - _componentRegistry: ArchitectureComponentRegistry│
│ - _modules: ArchitectureModules │ │ - _modules: ArchitectureModules │
│ - _logger: ILogger │ │ - _logger: ILogger │
│ │ │ │
│ + RegisterSystem<T>() │ │ + RegisterSystem<T>() │
│ + RegisterModel<T>() │ │ + RegisterModel<T>() │
│ + RegisterUtility<T>() │ │ + RegisterUtility<T>() │
│ + InstallModule() │ │ + InstallModule() │
│ + InitializeAsync() │ │ + InitializeAsync() │
│ + DestroyAsync() │ │ + DestroyAsync() │
│ + event PhaseChanged │ │ + event PhaseChanged │
└─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
@ -80,13 +80,13 @@ Architecture 采用以下设计模式:
│ - 上下文绑定 │ │ - 组件初始化 │ │ - Utility 注册│ │ │ │ - 上下文绑定 │ │ - 组件初始化 │ │ - Utility 注册│ │ │
│ - 容器冻结 │ │ - 就绪/销毁协调 │ │ - 生命周期接入│ │ │ │ - 容器冻结 │ │ - 就绪/销毁协调 │ │ - 生命周期接入│ │ │
└──────────────┘ └──────────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────────┘ └──────────────┘ └──────────────┘
``` ```
### 构造函数初始化 ### 构造函数初始化
从 v1.1.0 开始,所有管理器在构造函数中初始化: 从 v1.1.0 开始,所有管理器在构造函数中初始化:
```csharp ```csharp
protected Architecture( protected Architecture(
IArchitectureConfiguration? configuration = null, IArchitectureConfiguration? configuration = null,
IEnvironment? environment = null, IEnvironment? environment = null,
@ -109,94 +109,94 @@ protected Architecture(
_modules = new ArchitectureModules(this, resolvedServices, _logger); _modules = new ArchitectureModules(this, resolvedServices, _logger);
} }
``` ```
**优势**: **优势**:
- 消除 `null!` 断言,提高代码安全性 - 消除 `null!` 断言,提高代码安全性
- 对象在构造后立即可用 - 对象在构造后立即可用
- 符合"构造即完整"原则 - 符合"构造即完整"原则
- 可以在 InitializeAsync 之前访问事件 - 可以在 InitializeAsync 之前访问事件
## 生命周期管理 ## 生命周期管理
### 架构阶段 ### 架构阶段
Architecture 定义了 11 个生命周期阶段: Architecture 定义了 11 个生命周期阶段:
| 阶段 | 说明 | 触发时机 | | 阶段 | 说明 | 触发时机 |
|------------------------|--------------|------------------| |------------------------|--------------|------------------|
| `None` | 初始状态 | 构造函数完成后 | | `None` | 初始状态 | 构造函数完成后 |
| `BeforeUtilityInit` | Utility 初始化前 | 开始初始化 Utility | | `BeforeUtilityInit` | Utility 初始化前 | 开始初始化 Utility |
| `AfterUtilityInit` | Utility 初始化后 | 所有 Utility 初始化完成 | | `AfterUtilityInit` | Utility 初始化后 | 所有 Utility 初始化完成 |
| `BeforeModelInit` | Model 初始化前 | 开始初始化 Model | | `BeforeModelInit` | Model 初始化前 | 开始初始化 Model |
| `AfterModelInit` | Model 初始化后 | 所有 Model 初始化完成 | | `AfterModelInit` | Model 初始化后 | 所有 Model 初始化完成 |
| `BeforeSystemInit` | System 初始化前 | 开始初始化 System | | `BeforeSystemInit` | System 初始化前 | 开始初始化 System |
| `AfterSystemInit` | System 初始化后 | 所有 System 初始化完成 | | `AfterSystemInit` | System 初始化后 | 所有 System 初始化完成 |
| `Ready` | 就绪状态 | 所有组件初始化完成 | | `Ready` | 就绪状态 | 所有组件初始化完成 |
| `Destroying` | 销毁中 | 开始销毁 | | `Destroying` | 销毁中 | 开始销毁 |
| `Destroyed` | 已销毁 | 销毁完成 | | `Destroyed` | 已销毁 | 销毁完成 |
| `FailedInitialization` | 初始化失败 | 初始化过程中发生异常 | | `FailedInitialization` | 初始化失败 | 初始化过程中发生异常 |
### 阶段转换 ### 阶段转换
``` ```
正常流程: 正常流程:
None → BeforeUtilityInit → AfterUtilityInit → BeforeModelInit → AfterModelInit None → BeforeUtilityInit → AfterUtilityInit → BeforeModelInit → AfterModelInit
→ BeforeSystemInit → AfterSystemInit → Ready → Destroying → Destroyed → BeforeSystemInit → AfterSystemInit → Ready → Destroying → Destroyed
异常流程: 异常流程:
Any → FailedInitialization Any → FailedInitialization
``` ```
### 阶段事件 ### 阶段事件
可以通过 `PhaseChanged` 事件监听阶段变化: 可以通过 `PhaseChanged` 事件监听阶段变化:
```csharp ```csharp
public class MyArchitecture : Architecture public class MyArchitecture : Architecture
{ {
protected override void OnInitialize() protected override void OnInitialize()
{ {
// 监听阶段变化 // 监听阶段变化
PhaseChanged += phase => PhaseChanged += phase =>
{ {
Console.WriteLine($"Phase changed to: {phase}"); Console.WriteLine($"Phase changed to: {phase}");
}; };
} }
} }
``` ```
### 生命周期钩子 ### 生命周期钩子
实现 `IArchitectureLifecycleHook` 接口可以在阶段变化时执行自定义逻辑: 实现 `IArchitectureLifecycleHook` 接口可以在阶段变化时执行自定义逻辑:
```csharp ```csharp
public class MyLifecycleHook : IArchitectureLifecycleHook public class MyLifecycleHook : IArchitectureLifecycleHook
{ {
public void OnPhase(ArchitecturePhase phase, IArchitecture architecture) public void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
{ {
switch (phase) switch (phase)
{ {
case ArchitecturePhase.Ready: case ArchitecturePhase.Ready:
Console.WriteLine("Architecture is ready!"); Console.WriteLine("Architecture is ready!");
break; break;
case ArchitecturePhase.Destroying: case ArchitecturePhase.Destroying:
Console.WriteLine("Architecture is being destroyed!"); Console.WriteLine("Architecture is being destroyed!");
break; break;
} }
} }
} }
// 注册钩子 // 注册钩子
architecture.RegisterLifecycleHook(new MyLifecycleHook()); architecture.RegisterLifecycleHook(new MyLifecycleHook());
``` ```
### 初始化流程 ### 初始化流程
``` ```
1. 创建 Architecture 实例 1. 创建 Architecture 实例
└─> 构造函数初始化管理器 └─> 构造函数初始化管理器
2. 调用 InitializeAsync() 或 Initialize() 2. 调用 InitializeAsync() 或 Initialize()
├─> ArchitectureBootstrapper 准备基础设施 ├─> ArchitectureBootstrapper 准备基础设施
│ ├─> 初始化环境 (Environment.Initialize()) │ ├─> 初始化环境 (Environment.Initialize())
@ -211,14 +211,14 @@ architecture.RegisterLifecycleHook(new MyLifecycleHook());
│ └─> BeforeSystemInit → 初始化 System → AfterSystemInit │ └─> BeforeSystemInit → 初始化 System → AfterSystemInit
├─> CompleteInitialization() 冻结 IoC 容器 ├─> CompleteInitialization() 冻结 IoC 容器
└─> 进入 Ready 阶段 └─> 进入 Ready 阶段
3. 等待就绪 (可选) 3. 等待就绪 (可选)
└─> await architecture.WaitUntilReadyAsync() └─> await architecture.WaitUntilReadyAsync()
``` ```
### 销毁流程 ### 销毁流程
``` ```
1. 调用 DestroyAsync() 或 Destroy() 1. 调用 DestroyAsync() 或 Destroy()
├─> 检查当前阶段 (如果是 None 或已销毁则直接返回) ├─> 检查当前阶段 (如果是 None 或已销毁则直接返回)
├─> 进入 Destroying 阶段 ├─> 进入 Destroying 阶段
@ -229,11 +229,11 @@ architecture.RegisterLifecycleHook(new MyLifecycleHook());
├─> 进入 Destroyed 阶段 ├─> 进入 Destroyed 阶段
└─> 清空 IoC 容器 └─> 清空 IoC 容器
``` ```
--- ---
**版本**: 1.1.0 **版本**: 1.1.0
**更新日期**: 2026-03-17 **更新日期**: 2026-03-17
**相关文档**: **相关文档**:
- [核心框架概述](./index.md) - [核心框架概述](./index.md)

View File

@ -1,123 +1,123 @@
# Command 包使用说明 # Command 包使用说明
## 概述 ## 概述
Command 包实现了命令模式Command Pattern用于封装用户操作和业务逻辑。通过命令模式可以将请求封装为对象实现操作的参数化、队列化、日志记录、撤销等功能。 Command 包实现了命令模式Command Pattern用于封装用户操作和业务逻辑。通过命令模式可以将请求封装为对象实现操作的参数化、队列化、日志记录、撤销等功能。
命令系统是 GFramework CQRS 架构的重要组成部分,与事件系统和查询系统协同工作,实现完整的业务逻辑处理流程。 命令系统是 GFramework CQRS 架构的重要组成部分,与事件系统和查询系统协同工作,实现完整的业务逻辑处理流程。
## 核心接口 ## 核心接口
### ICommand ### ICommand
无返回值命令接口,定义了命令的基本契约。 无返回值命令接口,定义了命令的基本契约。
**核心方法:** **核心方法:**
```csharp ```csharp
void Execute(); // 执行命令 void Execute(); // 执行命令
``` ```
### ICommand`<TResult>` ### ICommand`<TResult>`
带返回值的命令接口,用于需要返回执行结果的命令。 带返回值的命令接口,用于需要返回执行结果的命令。
**核心方法:** **核心方法:**
```csharp ```csharp
TResult Execute(); // 执行命令并返回结果 TResult Execute(); // 执行命令并返回结果
``` ```
## 核心类 ## 核心类
### AbstractCommand ### AbstractCommand
无返回值命令的抽象基类,提供了命令的基础实现。它继承自 ContextAwareBase具有上下文感知能力。 无返回值命令的抽象基类,提供了命令的基础实现。它继承自 ContextAwareBase具有上下文感知能力。
**核心方法:** **核心方法:**
```csharp ```csharp
void ICommand.Execute(); // 实现 ICommand 接口 void ICommand.Execute(); // 实现 ICommand 接口
protected abstract void OnExecute(); // 抽象执行方法,由子类实现 protected abstract void OnExecute(); // 抽象执行方法,由子类实现
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 定义一个无返回值的基础命令 // 定义一个无返回值的基础命令
public class SimpleCommand : AbstractCommand public class SimpleCommand : AbstractCommand
{ {
protected override void OnExecute() protected override void OnExecute()
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
playerModel.Health.Value = playerModel.MaxHealth.Value; playerModel.Health.Value = playerModel.MaxHealth.Value;
this.SendEvent(new PlayerHealthRestoredEvent()); this.SendEvent(new PlayerHealthRestoredEvent());
} }
} }
// 使用命令 // 使用命令
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class GameController : IController public partial class GameController : IController
{ {
public void OnRestoreHealthButtonClicked() public void OnRestoreHealthButtonClicked()
{ {
this.SendCommand(new SimpleCommand()); this.SendCommand(new SimpleCommand());
} }
} }
``` ```
### AbstractCommand`<TResult>` ### AbstractCommand`<TResult>`
无输入参数但带返回值的命令基类。 无输入参数但带返回值的命令基类。
**核心方法:** **核心方法:**
```csharp ```csharp
TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口 TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
protected abstract TResult OnExecute(); // 抽象执行方法,由子类实现 protected abstract TResult OnExecute(); // 抽象执行方法,由子类实现
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 定义一个无输入但有返回值的命令 // 定义一个无输入但有返回值的命令
public class GetPlayerHealthQuery : AbstractCommand<int> public class GetPlayerHealthQuery : AbstractCommand<int>
{ {
protected override int OnExecute() protected override int OnExecute()
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
return playerModel.Health.Value; return playerModel.Health.Value;
} }
} }
// 使用命令 // 使用命令
public class UISystem : AbstractSystem public class UISystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<UpdateUIEvent>(OnUpdateUI); this.RegisterEvent<UpdateUIEvent>(OnUpdateUI);
} }
private void OnUpdateUI(UpdateUIEvent e) private void OnUpdateUI(UpdateUIEvent e)
{ {
var health = this.SendCommand(new GetPlayerHealthQuery()); var health = this.SendCommand(new GetPlayerHealthQuery());
Console.WriteLine($"Player health: {health}"); Console.WriteLine($"Player health: {health}");
} }
} }
``` ```
## 命令的生命周期 ## 命令的生命周期
1. **创建命令**:实例化命令对象,传入必要的参数 1. **创建命令**:实例化命令对象,传入必要的参数
2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()` 2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
3. **返回结果**:对于带返回值的命令,返回执行结果 3. **返回结果**:对于带返回值的命令,返回执行结果
4. **命令销毁**:命令执行完毕后可以被垃圾回收 4. **命令销毁**:命令执行完毕后可以被垃圾回收
**注意事项:** **注意事项:**
- 命令应该是无状态的,执行完即可丢弃 - 命令应该是无状态的,执行完即可丢弃
- 避免在命令中保存长期引用 - 避免在命令中保存长期引用
- 命令执行应该是原子操作 - 命令执行应该是原子操作
@ -145,331 +145,331 @@ public sealed class DamagePlayerCommand(int amount) : AbstractCommand
- Store 负责统一归约状态变化 - Store 负责统一归约状态变化
完整示例见 [`state-management`](./state-management)。 完整示例见 [`state-management`](./state-management)。
## CommandBus - 命令总线 ## CommandBus - 命令总线
### 功能说明 ### 功能说明
`CommandBus` 是命令执行的核心组件,负责发送和执行命令。 `CommandBus` 是命令执行的核心组件,负责发送和执行命令。
**主要方法:** **主要方法:**
```csharp ```csharp
void Send(ICommand command); // 发送无返回值命令 void Send(ICommand command); // 发送无返回值命令
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令 TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令
``` ```
**特点:** **特点:**
- 统一的命令执行入口 - 统一的命令执行入口
- 支持同步命令执行 - 支持同步命令执行
- 与架构上下文集成 - 与架构上下文集成
### 使用示例 ### 使用示例
```csharp ```csharp
// 通过架构获取命令总线 // 通过架构获取命令总线
var commandBus = architecture.Context.CommandBus; var commandBus = architecture.Context.CommandBus;
// 发送无返回值命令 // 发送无返回值命令
commandBus.Send(new StartGameCommand(1, "Player1")); commandBus.Send(new StartGameCommand(1, "Player1"));
// 发送带返回值命令 // 发送带返回值命令
var damage = commandBus.Send(new CalculateDamageCommand(100, 50)); var damage = commandBus.Send(new CalculateDamageCommand(100, 50));
``` ```
## 命令基类变体 ## 命令基类变体
框架提供了多种命令基类以满足不同需求: 框架提供了多种命令基类以满足不同需求:
### AbstractCommand`<TInput>` ### AbstractCommand`<TInput>`
带输入参数的无返回值命令类。通过 `ICommandInput` 接口传递参数。 带输入参数的无返回值命令类。通过 `ICommandInput` 接口传递参数。
**核心方法:** **核心方法:**
```csharp ```csharp
void ICommand.Execute(); // 实现 ICommand 接口 void ICommand.Execute(); // 实现 ICommand 接口
protected abstract void OnExecute(TInput input); // 抽象执行方法,接收输入参数 protected abstract void OnExecute(TInput input); // 抽象执行方法,接收输入参数
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 定义输入对象 // 定义输入对象
public class StartGameInput : ICommandInput public class StartGameInput : ICommandInput
{ {
public int LevelId { get; set; } public int LevelId { get; set; }
public string PlayerName { get; set; } public string PlayerName { get; set; }
} }
// 定义命令 // 定义命令
public class StartGameCommand : AbstractCommand<StartGameInput> public class StartGameCommand : AbstractCommand<StartGameInput>
{ {
protected override void OnExecute(StartGameInput input) protected override void OnExecute(StartGameInput input)
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
var gameModel = this.GetModel<GameModel>(); var gameModel = this.GetModel<GameModel>();
playerModel.PlayerName.Value = input.PlayerName; playerModel.PlayerName.Value = input.PlayerName;
gameModel.CurrentLevel.Value = input.LevelId; gameModel.CurrentLevel.Value = input.LevelId;
gameModel.GameState.Value = GameState.Playing; gameModel.GameState.Value = GameState.Playing;
this.SendEvent(new GameStartedEvent()); this.SendEvent(new GameStartedEvent());
} }
} }
// 使用命令 // 使用命令
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class GameController : IController public partial class GameController : IController
{ {
public void OnStartButtonClicked() public void OnStartButtonClicked()
{ {
var input = new StartGameInput { LevelId = 1, PlayerName = "Player1" }; var input = new StartGameInput { LevelId = 1, PlayerName = "Player1" };
this.SendCommand(new StartGameCommand { Input = input }); this.SendCommand(new StartGameCommand { Input = input });
} }
} }
``` ```
### AbstractCommand`<TInput, TResult>` ### AbstractCommand`<TInput, TResult>`
既带输入参数又带返回值的命令类。 既带输入参数又带返回值的命令类。
**核心方法:** **核心方法:**
```csharp ```csharp
TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口 TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
protected abstract TResult OnExecute(TInput input); // 抽象执行方法,接收输入参数 protected abstract TResult OnExecute(TInput input); // 抽象执行方法,接收输入参数
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 定义输入对象 // 定义输入对象
public class CalculateDamageInput : ICommandInput public class CalculateDamageInput : ICommandInput
{ {
public int AttackerAttackPower { get; set; } public int AttackerAttackPower { get; set; }
public int DefenderDefense { get; set; } public int DefenderDefense { get; set; }
} }
// 定义命令 // 定义命令
public class CalculateDamageCommand : AbstractCommand<CalculateDamageInput, int> public class CalculateDamageCommand : AbstractCommand<CalculateDamageInput, int>
{ {
protected override int OnExecute(CalculateDamageInput input) protected override int OnExecute(CalculateDamageInput input)
{ {
var config = this.GetModel<GameConfigModel>(); var config = this.GetModel<GameConfigModel>();
var baseDamage = input.AttackerAttackPower - input.DefenderDefense; var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier); var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
return (int)finalDamage; return (int)finalDamage;
} }
} }
// 使用命令 // 使用命令
public class CombatSystem : AbstractSystem public class CombatSystem : AbstractSystem
{ {
protected override void OnInit() { } protected override void OnInit() { }
public void Attack(Character attacker, Character defender) public void Attack(Character attacker, Character defender)
{ {
var input = new CalculateDamageInput var input = new CalculateDamageInput
{ {
AttackerAttackPower = attacker.AttackPower, AttackerAttackPower = attacker.AttackPower,
DefenderDefense = defender.Defense DefenderDefense = defender.Defense
}; };
var damage = this.SendCommand(new CalculateDamageCommand { Input = input }); var damage = this.SendCommand(new CalculateDamageCommand { Input = input });
defender.Health -= damage; defender.Health -= damage;
this.SendEvent(new DamageDealtEvent(attacker, defender, damage)); this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
} }
} }
``` ```
### AbstractAsyncCommand`<TInput>` ### AbstractAsyncCommand`<TInput>`
支持异步执行的带输入参数的无返回值命令基类。 支持异步执行的带输入参数的无返回值命令基类。
**核心方法:** **核心方法:**
```csharp ```csharp
Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口 Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口
protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法 protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法
``` ```
### AbstractAsyncCommand`<TInput, TResult>` ### AbstractAsyncCommand`<TInput, TResult>`
支持异步执行的既带输入参数又带返回值的命令基类。 支持异步执行的既带输入参数又带返回值的命令基类。
**核心方法:** **核心方法:**
```csharp ```csharp
Task<TResult> IAsyncCommand<TResult>.ExecuteAsync(); // 实现异步命令接口 Task<TResult> IAsyncCommand<TResult>.ExecuteAsync(); // 实现异步命令接口
protected abstract Task<TResult> OnExecuteAsync(TInput input); // 抽象异步执行方法 protected abstract Task<TResult> OnExecuteAsync(TInput input); // 抽象异步执行方法
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 定义输入对象 // 定义输入对象
public class LoadSaveDataInput : ICommandInput public class LoadSaveDataInput : ICommandInput
{ {
public string SaveSlot { get; set; } public string SaveSlot { get; set; }
} }
// 定义异步命令 // 定义异步命令
public class LoadSaveDataCommand : AbstractAsyncCommand<LoadSaveDataInput, SaveData> public class LoadSaveDataCommand : AbstractAsyncCommand<LoadSaveDataInput, SaveData>
{ {
protected override async Task<SaveData> OnExecuteAsync(LoadSaveDataInput input) protected override async Task<SaveData> OnExecuteAsync(LoadSaveDataInput input)
{ {
var storage = this.GetUtility<IStorageUtility>(); var storage = this.GetUtility<IStorageUtility>();
return await storage.LoadSaveDataAsync(input.SaveSlot); return await storage.LoadSaveDataAsync(input.SaveSlot);
} }
} }
// 使用异步命令 // 使用异步命令
public class SaveSystem : AbstractSystem public class SaveSystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<LoadGameRequestEvent>(OnLoadGameRequest); this.RegisterEvent<LoadGameRequestEvent>(OnLoadGameRequest);
} }
private async void OnLoadGameRequest(LoadGameRequestEvent e) private async void OnLoadGameRequest(LoadGameRequestEvent e)
{ {
var input = new LoadSaveDataInput { SaveSlot = e.SaveSlot }; var input = new LoadSaveDataInput { SaveSlot = e.SaveSlot };
var saveData = await this.SendCommandAsync(new LoadSaveDataCommand { Input = input }); var saveData = await this.SendCommandAsync(new LoadSaveDataCommand { Input = input });
if (saveData != null) if (saveData != null)
{ {
this.SendEvent(new GameLoadedEvent { SaveData = saveData }); this.SendEvent(new GameLoadedEvent { SaveData = saveData });
} }
} }
} }
``` ```
## 命令处理器执行 ## 命令处理器执行
所有发送给命令总线的命令最终都会通过 `CommandExecutor` 来执行: 所有发送给命令总线的命令最终都会通过 `CommandExecutor` 来执行:
```csharp ```csharp
public class CommandExecutor public class CommandExecutor
{ {
public static void Execute(ICommand command) public static void Execute(ICommand command)
{ {
command.Execute(); command.Execute();
} }
public static TResult Execute<TResult>(ICommand<TResult> command) public static TResult Execute<TResult>(ICommand<TResult> command)
{ {
return command.Execute(); return command.Execute();
} }
} }
``` ```
**特点:** **特点:**
- 提供统一的命令执行机制 - 提供统一的命令执行机制
- 支持同步和异步命令执行 - 支持同步和异步命令执行
- 可以扩展添加中间件逻辑 - 可以扩展添加中间件逻辑
## 使用场景 ## 使用场景
### 1. 用户交互操作 ### 1. 用户交互操作
```csharp ```csharp
public class SaveGameCommand : AbstractCommand public class SaveGameCommand : AbstractCommand
{ {
private readonly string _saveSlot; private readonly string _saveSlot;
public SaveGameCommand(string saveSlot) public SaveGameCommand(string saveSlot)
{ {
_saveSlot = saveSlot; _saveSlot = saveSlot;
} }
protected override void OnExecute() protected override void OnExecute()
{ {
var saveSystem = this.GetSystem<SaveSystem>(); var saveSystem = this.GetSystem<SaveSystem>();
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
saveSystem.SavePlayerData(playerModel, _saveSlot); saveSystem.SavePlayerData(playerModel, _saveSlot);
this.SendEvent(new GameSavedEvent(_saveSlot)); this.SendEvent(new GameSavedEvent(_saveSlot));
} }
} }
``` ```
### 2. 业务流程控制 ### 2. 业务流程控制
```csharp ```csharp
public class LoadLevelCommand : AbstractCommand public class LoadLevelCommand : AbstractCommand
{ {
private readonly int _levelId; private readonly int _levelId;
public LoadLevelCommand(int levelId) public LoadLevelCommand(int levelId)
{ {
_levelId = levelId; _levelId = levelId;
} }
protected override void OnExecute() protected override void OnExecute()
{ {
var levelSystem = this.GetSystem<LevelSystem>(); var levelSystem = this.GetSystem<LevelSystem>();
var uiSystem = this.GetSystem<UISystem>(); var uiSystem = this.GetSystem<UISystem>();
// 显示加载界面 // 显示加载界面
uiSystem.ShowLoadingScreen(); uiSystem.ShowLoadingScreen();
// 加载关卡 // 加载关卡
levelSystem.LoadLevel(_levelId); levelSystem.LoadLevel(_levelId);
// 发送事件 // 发送事件
this.SendEvent(new LevelLoadedEvent(_levelId)); this.SendEvent(new LevelLoadedEvent(_levelId));
} }
} }
``` ```
## 最佳实践 ## 最佳实践
1. **保持命令原子性**:一个命令应该完成一个完整的业务操作 1. **保持命令原子性**:一个命令应该完成一个完整的业务操作
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃 2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入 3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信 4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
5. **合理使用返回值**:只在确实需要返回结果时使用带返回值的命令 5. **合理使用返回值**:只在确实需要返回结果时使用带返回值的命令
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand` 6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand`
7. **单一职责原则**:每个命令只负责一个特定的业务操作 7. **单一职责原则**:每个命令只负责一个特定的业务操作
8. **使用异步命令**:对于需要长时间执行的操作,使用异步命令避免阻塞 8. **使用异步命令**:对于需要长时间执行的操作,使用异步命令避免阻塞
9. **命令验证**:在命令执行前验证输入参数的有效性 9. **命令验证**:在命令执行前验证输入参数的有效性
10. **错误处理**:在命令中适当处理异常情况 10. **错误处理**:在命令中适当处理异常情况
## 命令模式优势 ## 命令模式优势
### 1. 可扩展性 ### 1. 可扩展性
- 命令可以被序列化和存储 - 命令可以被序列化和存储
- 支持命令队列和批处理 - 支持命令队列和批处理
- 便于实现撤销/重做功能 - 便于实现撤销/重做功能
### 2. 可测试性 ### 2. 可测试性
- 命令逻辑独立,易于单元测试 - 命令逻辑独立,易于单元测试
- 可以模拟命令执行结果 - 可以模拟命令执行结果
- 支持行为驱动开发 - 支持行为驱动开发
### 3. 可维护性 ### 3. 可维护性
- 业务逻辑集中管理 - 业务逻辑集中管理
- 降低组件间耦合度 - 降低组件间耦合度
- 便于重构和扩展 - 便于重构和扩展
## 相关包 ## 相关包
- [`architecture`](./architecture.md) - 架构核心,负责命令的分发和执行 - [`architecture`](./architecture.md) - 架构核心,负责命令的分发和执行
- [`extensions`](./extensions.md) - 提供 `SendCommand()` 扩展方法 - [`extensions`](./extensions.md) - 提供 `SendCommand()` 扩展方法
- [`query`](./query.md) - 查询模式,用于数据查询 - [`query`](./query.md) - 查询模式,用于数据查询
- [`events`](./events.md) - 事件系统,命令执行后的通知机制 - [`events`](./events.md) - 事件系统,命令执行后的通知机制
- [`system`](./system.md) - 业务系统,命令的主要执行者 - [`system`](./system.md) - 业务系统,命令的主要执行者
- [`model`](./model.md) - 数据模型,命令操作的数据 - [`model`](./model.md) - 数据模型,命令操作的数据
--- ---
**许可证**Apache 2.0 **许可证**Apache 2.0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
# Property 包使用说明 # Property 包使用说明
## 概述 ## 概述
Property 包提供了可绑定属性BindableProperty的实现支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。 Property 包提供了可绑定属性BindableProperty的实现支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。
BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。 BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。
@ -9,137 +9,137 @@ BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事
> 对于简单字段和局部 UI 绑定,`BindableProperty<T>` 仍然是首选方案。 > 对于简单字段和局部 UI 绑定,`BindableProperty<T>` 仍然是首选方案。
> 如果你需要统一管理复杂状态树、通过 action / reducer 演进状态,或复用局部状态选择器, > 如果你需要统一管理复杂状态树、通过 action / reducer 演进状态,或复用局部状态选择器,
> 请同时参考 [`state-management`](./state-management)。 > 请同时参考 [`state-management`](./state-management)。
## 核心接口 ## 核心接口
### IReadonlyBindableProperty`<T>` ### IReadonlyBindableProperty`<T>`
只读可绑定属性接口,提供属性值的读取和变更监听功能。 只读可绑定属性接口,提供属性值的读取和变更监听功能。
**核心成员:** **核心成员:**
```csharp ```csharp
// 获取属性值 // 获取属性值
T Value { get; } T Value { get; }
// 注册监听(不立即触发回调) // 注册监听(不立即触发回调)
IUnRegister Register(Action<T> onValueChanged); IUnRegister Register(Action<T> onValueChanged);
// 注册监听并立即触发回调传递当前值 // 注册监听并立即触发回调传递当前值
IUnRegister RegisterWithInitValue(Action<T> action); IUnRegister RegisterWithInitValue(Action<T> action);
// 取消监听 // 取消监听
void UnRegister(Action<T> onValueChanged); void UnRegister(Action<T> onValueChanged);
``` ```
### IBindableProperty`<T>` ### IBindableProperty`<T>`
可绑定属性接口,继承自只读接口,增加了修改能力。 可绑定属性接口,继承自只读接口,增加了修改能力。
**核心成员:** **核心成员:**
```csharp ```csharp
// 可读写的属性值 // 可读写的属性值
new T Value { get; set; } new T Value { get; set; }
// 设置值但不触发事件 // 设置值但不触发事件
void SetValueWithoutEvent(T newValue); void SetValueWithoutEvent(T newValue);
``` ```
## 核心类 ## 核心类
### BindableProperty`<T>` ### BindableProperty`<T>`
可绑定属性的完整实现。 可绑定属性的完整实现。
**核心方法:** **核心方法:**
```csharp ```csharp
// 构造函数 // 构造函数
BindableProperty(T defaultValue = default!); BindableProperty(T defaultValue = default!);
// 属性值 // 属性值
T Value { get; set; } T Value { get; set; }
// 注册监听 // 注册监听
IUnRegister Register(Action<T> onValueChanged); IUnRegister Register(Action<T> onValueChanged);
IUnRegister RegisterWithInitValue(Action<T> action); IUnRegister RegisterWithInitValue(Action<T> action);
// 取消监听 // 取消监听
void UnRegister(Action<T> onValueChanged); void UnRegister(Action<T> onValueChanged);
// 设置值但不触发事件 // 设置值但不触发事件
void SetValueWithoutEvent(T newValue); void SetValueWithoutEvent(T newValue);
// 设置自定义比较器 // 设置自定义比较器
BindableProperty<T> WithComparer(Func<T, T, bool> comparer); BindableProperty<T> WithComparer(Func<T, T, bool> comparer);
``` ```
**使用示例:** **使用示例:**
```csharp ```csharp
// 创建可绑定属性 // 创建可绑定属性
var health = new BindableProperty<int>(100); var health = new BindableProperty<int>(100);
// 监听值变化(不会立即触发) // 监听值变化(不会立即触发)
var unregister = health.Register(newValue => var unregister = health.Register(newValue =>
{ {
Console.WriteLine($"Health changed to: {newValue}"); Console.WriteLine($"Health changed to: {newValue}");
}); });
// 设置值(会触发监听器) // 设置值(会触发监听器)
health.Value = 50; // 输出: Health changed to: 50 health.Value = 50; // 输出: Health changed to: 50
// 取消监听 // 取消监听
unregister.UnRegister(); unregister.UnRegister();
// 设置值但不触发事件 // 设置值但不触发事件
health.SetValueWithoutEvent(75); health.SetValueWithoutEvent(75);
``` ```
**高级功能:** **高级功能:**
```csharp ```csharp
// 1. 注册并立即获得当前值 // 1. 注册并立即获得当前值
health.RegisterWithInitValue(value => health.RegisterWithInitValue(value =>
{ {
Console.WriteLine($"Current health: {value}"); // 立即输出当前值 Console.WriteLine($"Current health: {value}"); // 立即输出当前值
// 后续值变化时也会调用 // 后续值变化时也会调用
}); });
// 2. 自定义比较器(静态方法) // 2. 自定义比较器(静态方法)
BindableProperty<int>.Comparer = (a, b) => Math.Abs(a - b) < 1; BindableProperty<int>.Comparer = (a, b) => Math.Abs(a - b) < 1;
// 3. 使用实例方法设置比较器 // 3. 使用实例方法设置比较器
var position = new BindableProperty<Vector3>(Vector3.Zero) var position = new BindableProperty<Vector3>(Vector3.Zero)
.WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等 .WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等
// 4. 字符串比较器示例 // 4. 字符串比较器示例
var name = new BindableProperty<string>("Player") var name = new BindableProperty<string>("Player")
.WithComparer((a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); .WithComparer((a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase));
``` ```
### BindablePropertyUnRegister`<T>` ### BindablePropertyUnRegister`<T>`
可绑定属性的注销器,负责清理监听。 可绑定属性的注销器,负责清理监听。
**使用示例:** **使用示例:**
```csharp ```csharp
var unregister = health.Register(OnHealthChanged); var unregister = health.Register(OnHealthChanged);
// 当需要取消监听时 // 当需要取消监听时
unregister.UnRegister(); unregister.UnRegister();
``` ```
## BindableProperty 工作原理 ## BindableProperty 工作原理
BindableProperty 基于事件系统实现属性变化通知: BindableProperty 基于事件系统实现属性变化通知:
1. **值设置**:当设置 `Value` 属性时,首先进行值比较 1. **值设置**:当设置 `Value` 属性时,首先进行值比较
2. **变化检测**:使用 `EqualityComparer<T>.Default` 或自定义比较器检测值变化 2. **变化检测**:使用 `EqualityComparer<T>.Default` 或自定义比较器检测值变化
3. **事件触发**:如果值发生变化,调用所有注册的回调函数 3. **事件触发**:如果值发生变化,调用所有注册的回调函数
4. **内存管理**:通过 `IUnRegister` 机制管理监听器的生命周期 4. **内存管理**:通过 `IUnRegister` 机制管理监听器的生命周期
## 在 Model 中使用 ## 在 Model 中使用
### 什么时候继续使用 BindableProperty ### 什么时候继续使用 BindableProperty
@ -178,285 +178,285 @@ public class PlayerStateModel : AbstractModel
public sealed record PlayerState(int Health, string Name); public sealed record PlayerState(int Health, string Name);
public sealed record DamageAction(int Amount); public sealed record DamageAction(int Amount);
``` ```
### 定义可绑定属性 ### 定义可绑定属性
```csharp ```csharp
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// 可读写属性 // 可读写属性
public BindableProperty<string> Name { get; } = new("Player"); public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1); public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100); public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<Vector3> Position { get; } = new(Vector3.Zero); public BindableProperty<Vector3> Position { get; } = new(Vector3.Zero);
// 只读属性(外部只能读取和监听) // 只读属性(外部只能读取和监听)
public IReadonlyBindableProperty<int> ReadonlyHealth => Health; public IReadonlyBindableProperty<int> ReadonlyHealth => Health;
protected override void OnInit() protected override void OnInit()
{ {
// 内部监听属性变化 // 内部监听属性变化
Health.Register(hp => Health.Register(hp =>
{ {
if (hp <= 0) if (hp <= 0)
{ {
this.SendEvent(new PlayerDiedEvent()); this.SendEvent(new PlayerDiedEvent());
} }
else if (hp < MaxHealth.Value * 0.3f) else if (hp < MaxHealth.Value * 0.3f)
{ {
this.SendEvent(new LowHealthWarningEvent()); this.SendEvent(new LowHealthWarningEvent());
} }
}); });
// 监听等级变化 // 监听等级变化
Level.Register(newLevel => Level.Register(newLevel =>
{ {
this.SendEvent(new PlayerLevelUpEvent { NewLevel = newLevel }); this.SendEvent(new PlayerLevelUpEvent { NewLevel = newLevel });
}); });
} }
// 业务方法 // 业务方法
public void TakeDamage(int damage) public void TakeDamage(int damage)
{ {
Health.Value = Math.Max(0, Health.Value - damage); Health.Value = Math.Max(0, Health.Value - damage);
} }
public void Heal(int amount) public void Heal(int amount)
{ {
Health.Value = Math.Min(MaxHealth.Value, Health.Value + amount); Health.Value = Math.Min(MaxHealth.Value, Health.Value + amount);
} }
public float GetHealthPercentage() public float GetHealthPercentage()
{ {
return (float)Health.Value / MaxHealth.Value; return (float)Health.Value / MaxHealth.Value;
} }
} }
``` ```
## 在 Controller 中监听 ## 在 Controller 中监听
### UI 数据绑定 ### UI 数据绑定
```csharp ```csharp
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class PlayerUI : Control, IController public partial class PlayerUI : Control, IController
{ {
[Export] private Label _healthLabel; [Export] private Label _healthLabel;
[Export] private Label _nameLabel; [Export] private Label _nameLabel;
[Export] private ProgressBar _healthBar; [Export] private ProgressBar _healthBar;
private IUnRegisterList _unregisterList = new UnRegisterList(); private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready() public override void _Ready()
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
// 绑定生命值到UI立即显示当前值 // 绑定生命值到UI立即显示当前值
playerModel.Health playerModel.Health
.RegisterWithInitValue(health => .RegisterWithInitValue(health =>
{ {
_healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}"; _healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}";
_healthBar.Value = (float)health / playerModel.MaxHealth.Value * 100; _healthBar.Value = (float)health / playerModel.MaxHealth.Value * 100;
}) })
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
// 绑定最大生命值 // 绑定最大生命值
playerModel.MaxHealth playerModel.MaxHealth
.RegisterWithInitValue(maxHealth => .RegisterWithInitValue(maxHealth =>
{ {
_healthBar.MaxValue = maxHealth; _healthBar.MaxValue = maxHealth;
}) })
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
// 绑定名称 // 绑定名称
playerModel.Name playerModel.Name
.RegisterWithInitValue(name => .RegisterWithInitValue(name =>
{ {
_nameLabel.Text = name; _nameLabel.Text = name;
}) })
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
// 绑定位置(仅用于调试显示) // 绑定位置(仅用于调试显示)
playerModel.Position playerModel.Position
.RegisterWithInitValue(pos => .RegisterWithInitValue(pos =>
{ {
// 仅在调试模式下显示 // 仅在调试模式下显示
#if DEBUG #if DEBUG
Console.WriteLine($"Player position: {pos}"); Console.WriteLine($"Player position: {pos}");
#endif #endif
}) })
.AddToUnregisterList(_unregisterList); .AddToUnregisterList(_unregisterList);
} }
public override void _ExitTree() public override void _ExitTree()
{ {
_unregisterList.UnRegisterAll(); _unregisterList.UnRegisterAll();
} }
} }
``` ```
## 常见使用模式 ## 常见使用模式
### 1. 双向绑定 ### 1. 双向绑定
```c# ```c#
// Model // Model
public class SettingsModel : AbstractModel public class SettingsModel : AbstractModel
{ {
public BindableProperty<float> MasterVolume { get; } = new(1.0f); public BindableProperty<float> MasterVolume { get; } = new(1.0f);
protected override void OnInit() { } protected override void OnInit() { }
} }
// UI Controller // UI Controller
[ContextAware] [ContextAware]
public partial class VolumeSlider : HSlider, IController public partial class VolumeSlider : HSlider, IController
{ {
private BindableProperty<float> _volumeProperty; private BindableProperty<float> _volumeProperty;
public override void _Ready() public override void _Ready()
{ {
_volumeProperty = this.GetModel<SettingsModel>().MasterVolume; _volumeProperty = this.GetModel<SettingsModel>().MasterVolume;
// Model -> UI // Model -> UI
_volumeProperty.RegisterWithInitValue(vol => Value = vol) _volumeProperty.RegisterWithInitValue(vol => Value = vol)
.UnRegisterWhenNodeExitTree(this); .UnRegisterWhenNodeExitTree(this);
// UI -> Model // UI -> Model
ValueChanged += newValue => _volumeProperty.Value = (float)newValue; ValueChanged += newValue => _volumeProperty.Value = (float)newValue;
} }
} }
``` ```
### 2. 计算属性 ### 2. 计算属性
```c# ```c#
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100); public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<float> HealthPercent { get; } = new(1.0f); public BindableProperty<float> HealthPercent { get; } = new(1.0f);
protected override void OnInit() protected override void OnInit()
{ {
// 自动计算百分比 // 自动计算百分比
Action updatePercent = () => Action updatePercent = () =>
{ {
HealthPercent.Value = (float)Health.Value / MaxHealth.Value; HealthPercent.Value = (float)Health.Value / MaxHealth.Value;
}; };
Health.Register(_ => updatePercent()); Health.Register(_ => updatePercent());
MaxHealth.Register(_ => updatePercent()); MaxHealth.Register(_ => updatePercent());
updatePercent(); // 初始计算 updatePercent(); // 初始计算
} }
} }
``` ```
### 3. 属性验证 ### 3. 属性验证
```c# ```c#
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
private BindableProperty<int> _health = new(100); private BindableProperty<int> _health = new(100);
public BindableProperty<int> Health public BindableProperty<int> Health
{ {
get => _health; get => _health;
set set
{ {
// 限制范围 // 限制范围
var clampedValue = Math.Clamp(value.Value, 0, MaxHealth.Value); var clampedValue = Math.Clamp(value.Value, 0, MaxHealth.Value);
_health.Value = clampedValue; _health.Value = clampedValue;
} }
} }
public BindableProperty<int> MaxHealth { get; } = new(100); public BindableProperty<int> MaxHealth { get; } = new(100);
protected override void OnInit() { } protected override void OnInit() { }
} }
``` ```
### 4. 条件监听 ### 4. 条件监听
```c# ```c#
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class CombatController : Node, IController public partial class CombatController : Node, IController
{ {
public override void _Ready() public override void _Ready()
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
// 只在生命值低于30%时显示警告 // 只在生命值低于30%时显示警告
playerModel.Health.Register(hp => playerModel.Health.Register(hp =>
{ {
if (hp < playerModel.MaxHealth.Value * 0.3f) if (hp < playerModel.MaxHealth.Value * 0.3f)
{ {
ShowLowHealthWarning(); ShowLowHealthWarning();
} }
else else
{ {
HideLowHealthWarning(); HideLowHealthWarning();
} }
}).UnRegisterWhenNodeExitTree(this); }).UnRegisterWhenNodeExitTree(this);
} }
} }
``` ```
## 性能优化 ## 性能优化
### 1. 避免频繁触发 ### 1. 避免频繁触发
```c# ```c#
// 使用 SetValueWithoutEvent 批量修改 // 使用 SetValueWithoutEvent 批量修改
public void LoadPlayerData(SaveData data) public void LoadPlayerData(SaveData data)
{ {
// 临时关闭事件 // 临时关闭事件
Health.SetValueWithoutEvent(data.Health); Health.SetValueWithoutEvent(data.Health);
Mana.SetValueWithoutEvent(data.Mana); Mana.SetValueWithoutEvent(data.Mana);
Gold.SetValueWithoutEvent(data.Gold); Gold.SetValueWithoutEvent(data.Gold);
// 最后统一触发一次更新事件 // 最后统一触发一次更新事件
this.SendEvent(new PlayerDataLoadedEvent()); this.SendEvent(new PlayerDataLoadedEvent());
} }
``` ```
### 2. 自定义比较器 ### 2. 自定义比较器
```c# ```c#
// 避免浮点数精度问题导致的频繁触发 // 避免浮点数精度问题导致的频繁触发
var position = new BindableProperty<Vector3>() var position = new BindableProperty<Vector3>()
.WithComparer((a, b) => a.DistanceTo(b) < 0.001f); .WithComparer((a, b) => a.DistanceTo(b) < 0.001f);
``` ```
## 实现原理 ## 实现原理
### 值变化检测 ### 值变化检测
```c# ```c#
// 使用 EqualityComparer<T>.Default 进行比较 // 使用 EqualityComparer<T>.Default 进行比较
if (!EqualityComparer<T>.Default.Equals(value, MValue)) if (!EqualityComparer<T>.Default.Equals(value, MValue))
{ {
MValue = value; MValue = value;
_mOnValueChanged?.Invoke(value); _mOnValueChanged?.Invoke(value);
} }
``` ```
### 事件触发机制 ### 事件触发机制
```c# ```c#
// 当值变化时触发所有注册的回调 // 当值变化时触发所有注册的回调
_mOnValueChanged?.Invoke(value); _mOnValueChanged?.Invoke(value);
``` ```
## 最佳实践 ## 最佳实践
1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层 1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层
2. **使用只读接口暴露** - 防止外部随意修改 2. **使用只读接口暴露** - 防止外部随意修改
3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree 3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree
@ -464,14 +464,14 @@ _mOnValueChanged?.Invoke(value);
5. **避免循环依赖** - 属性监听器中修改其他属性要小心 5. **避免循环依赖** - 属性监听器中修改其他属性要小心
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性 6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
7. **复杂聚合状态使用 Store** - 当多个字段必须统一演进时,使用 Store 管理聚合状态更清晰 7. **复杂聚合状态使用 Store** - 当多个字段必须统一演进时,使用 Store 管理聚合状态更清晰
## 相关包 ## 相关包
- [`model`](./model.md) - Model 中大量使用 BindableProperty - [`model`](./model.md) - Model 中大量使用 BindableProperty
- [`events`](./events.md) - BindableProperty 基于事件系统实现 - [`events`](./events.md) - BindableProperty 基于事件系统实现
- [`state-management`](./state-management) - 复杂状态树的集中式管理方案 - [`state-management`](./state-management) - 复杂状态树的集中式管理方案
- [`extensions`](./extensions.md) - 提供便捷的注销扩展方法 - [`extensions`](./extensions.md) - 提供便捷的注销扩展方法
--- ---
**许可证**: Apache 2.0 **许可证**: Apache 2.0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,323 +1,323 @@
# 快速开始 # 快速开始
本指南将帮助您快速构建第一个基于 GFramework 的应用程序。 本指南将帮助您快速构建第一个基于 GFramework 的应用程序。
## 1. 创建项目架构 ## 1. 创建项目架构
首先定义您的应用架构: 首先定义您的应用架构:
```csharp ```csharp
using GFramework.Core.Architecture; using GFramework.Core.Architecture;
public class GameArchitecture : Architecture public class GameArchitecture : Architecture
{ {
protected override void Init() protected override void Init()
{ {
// 注册模型 - 存储应用状态 // 注册模型 - 存储应用状态
RegisterModel(new PlayerModel()); RegisterModel(new PlayerModel());
RegisterModel(new GameStateModel()); RegisterModel(new GameStateModel());
// 注册系统 - 处理业务逻辑 // 注册系统 - 处理业务逻辑
RegisterSystem(new PlayerSystem()); RegisterSystem(new PlayerSystem());
RegisterSystem(new GameLogicSystem()); RegisterSystem(new GameLogicSystem());
// 注册工具类 - 提供辅助功能 // 注册工具类 - 提供辅助功能
RegisterUtility(new StorageUtility()); RegisterUtility(new StorageUtility());
} }
} }
``` ```
## 2. 定义数据模型 ## 2. 定义数据模型
创建您的数据模型: 创建您的数据模型:
```csharp ```csharp
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// 使用可绑定属性实现响应式数据 // 使用可绑定属性实现响应式数据
public BindableProperty<string> Name { get; } = new("Player"); public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Score { get; } = new(0); public BindableProperty<int> Score { get; } = new(0);
protected override void OnInit() protected override void OnInit()
{ {
// 监听健康值变化 // 监听健康值变化
Health.Register(OnHealthChanged); Health.Register(OnHealthChanged);
} }
private void OnHealthChanged(int newHealth) private void OnHealthChanged(int newHealth)
{ {
if (newHealth <= 0) if (newHealth <= 0)
{ {
this.SendEvent(new PlayerDiedEvent()); this.SendEvent(new PlayerDiedEvent());
} }
} }
} }
public class GameStateModel : AbstractModel public class GameStateModel : AbstractModel
{ {
public BindableProperty<bool> IsGameRunning { get; } = new(false); public BindableProperty<bool> IsGameRunning { get; } = new(false);
public BindableProperty<int> CurrentLevel { get; } = new(1); public BindableProperty<int> CurrentLevel { get; } = new(1);
} }
``` ```
## 3. 实现业务逻辑 ## 3. 实现业务逻辑
创建处理业务逻辑的系统: 创建处理业务逻辑的系统:
```csharp ```csharp
public class PlayerSystem : AbstractSystem public class PlayerSystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
// 监听玩家输入事件 // 监听玩家输入事件
this.RegisterEvent<PlayerMoveEvent>(OnPlayerMove); this.RegisterEvent<PlayerMoveEvent>(OnPlayerMove);
this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack); this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack);
} }
private void OnPlayerMove(PlayerMoveEvent e) private void OnPlayerMove(PlayerMoveEvent e)
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
// 处理移动逻辑 // 处理移动逻辑
Console.WriteLine($"Player moved to {e.Direction}"); Console.WriteLine($"Player moved to {e.Direction}");
} }
private void OnPlayerAttack(PlayerAttackEvent e) private void OnPlayerAttack(PlayerAttackEvent e)
{ {
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
// 处理攻击逻辑 // 处理攻击逻辑
playerModel.Score.Value += 10; playerModel.Score.Value += 10;
this.SendEvent(new EnemyDamagedEvent { Damage = 25 }); this.SendEvent(new EnemyDamagedEvent { Damage = 25 });
} }
} }
public class GameLogicSystem : AbstractSystem public class GameLogicSystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
this.RegisterEvent<EnemyDamagedEvent>(OnEnemyDamaged); this.RegisterEvent<EnemyDamagedEvent>(OnEnemyDamaged);
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied); this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
} }
private void OnEnemyDamaged(EnemyDamagedEvent e) private void OnEnemyDamaged(EnemyDamagedEvent e)
{ {
Console.WriteLine($"Enemy took {e.Damage} damage"); Console.WriteLine($"Enemy took {e.Damage} damage");
// 检查是否需要升级关卡 // 检查是否需要升级关卡
CheckLevelProgress(); CheckLevelProgress();
} }
private void OnPlayerDied(PlayerDiedEvent e) private void OnPlayerDied(PlayerDiedEvent e)
{ {
var gameState = this.GetModel<GameStateModel>(); var gameState = this.GetModel<GameStateModel>();
gameState.IsGameRunning.Value = false; gameState.IsGameRunning.Value = false;
Console.WriteLine("Game Over!"); Console.WriteLine("Game Over!");
} }
private void CheckLevelProgress() private void CheckLevelProgress()
{ {
// 实现关卡进度检查逻辑 // 实现关卡进度检查逻辑
} }
} }
``` ```
## 4. 定义事件 ## 4. 定义事件
创建应用中使用的事件: 创建应用中使用的事件:
```csharp ```csharp
public class PlayerMoveEvent : IEvent public class PlayerMoveEvent : IEvent
{ {
public Vector2 Direction { get; set; } public Vector2 Direction { get; set; }
} }
public class PlayerAttackEvent : IEvent public class PlayerAttackEvent : IEvent
{ {
public Vector2 TargetPosition { get; set; } public Vector2 TargetPosition { get; set; }
} }
public class PlayerDiedEvent : IEvent public class PlayerDiedEvent : IEvent
{ {
// 玩家死亡事件 // 玩家死亡事件
} }
public class EnemyDamagedEvent : IEvent public class EnemyDamagedEvent : IEvent
{ {
public int Damage { get; set; } public int Damage { get; set; }
} }
``` ```
## 5. 创建控制器 ## 5. 创建控制器
实现控制器来连接 UI 和业务逻辑: 实现控制器来连接 UI 和业务逻辑:
```csharp ```csharp
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class GameController : IController public partial class GameController : IController
{ {
private PlayerModel _playerModel; private PlayerModel _playerModel;
private GameStateModel _gameStateModel; private GameStateModel _gameStateModel;
public void Initialize() public void Initialize()
{ {
_playerModel = this.GetModel<PlayerModel>(); _playerModel = this.GetModel<PlayerModel>();
_gameStateModel = this.GetModel<GameStateModel>(); _gameStateModel = this.GetModel<GameStateModel>();
// 初始化事件监听 // 初始化事件监听
InitializeEventListeners(); InitializeEventListeners();
} }
private void InitializeEventListeners() private void InitializeEventListeners()
{ {
// 监听模型变化并更新 UI // 监听模型变化并更新 UI
_playerModel.Health.RegisterWithInitValue(OnHealthChanged); _playerModel.Health.RegisterWithInitValue(OnHealthChanged);
_playerModel.Score.RegisterWithInitValue(OnScoreChanged); _playerModel.Score.RegisterWithInitValue(OnScoreChanged);
_gameStateModel.IsGameRunning.Register(OnGameStateChanged); _gameStateModel.IsGameRunning.Register(OnGameStateChanged);
} }
public void StartGame() public void StartGame()
{ {
_gameStateModel.IsGameRunning.Value = true; _gameStateModel.IsGameRunning.Value = true;
this.SendEvent(new GameStartEvent()); this.SendEvent(new GameStartEvent());
Console.WriteLine("Game started!"); Console.WriteLine("Game started!");
} }
public void MovePlayer(Vector2 direction) public void MovePlayer(Vector2 direction)
{ {
this.SendCommand(new MovePlayerCommand { Direction = direction }); this.SendCommand(new MovePlayerCommand { Direction = direction });
} }
public void PlayerAttack(Vector2 target) public void PlayerAttack(Vector2 target)
{ {
this.SendCommand(new AttackCommand { TargetPosition = target }); this.SendCommand(new AttackCommand { TargetPosition = target });
} }
// UI 更新回调 // UI 更新回调
private void OnHealthChanged(int health) private void OnHealthChanged(int health)
{ {
UpdateHealthDisplay(health); UpdateHealthDisplay(health);
} }
private void OnScoreChanged(int score) private void OnScoreChanged(int score)
{ {
UpdateScoreDisplay(score); UpdateScoreDisplay(score);
} }
private void OnGameStateChanged(bool isRunning) private void OnGameStateChanged(bool isRunning)
{ {
UpdateGameStatusDisplay(isRunning); UpdateGameStatusDisplay(isRunning);
} }
private void UpdateHealthDisplay(int health) private void UpdateHealthDisplay(int health)
{ {
// 更新血条 UI // 更新血条 UI
Console.WriteLine($"Health: {health}"); Console.WriteLine($"Health: {health}");
} }
private void UpdateScoreDisplay(int score) private void UpdateScoreDisplay(int score)
{ {
// 更新分数显示 // 更新分数显示
Console.WriteLine($"Score: {score}"); Console.WriteLine($"Score: {score}");
} }
private void UpdateGameStatusDisplay(bool isRunning) private void UpdateGameStatusDisplay(bool isRunning)
{ {
// 更新游戏状态显示 // 更新游戏状态显示
Console.WriteLine($"Game running: {isRunning}"); Console.WriteLine($"Game running: {isRunning}");
} }
} }
``` ```
## 6. 定义命令 ## 6. 定义命令
创建命令来封装用户操作: 创建命令来封装用户操作:
```csharp ```csharp
public class MovePlayerCommand : AbstractCommand public class MovePlayerCommand : AbstractCommand
{ {
public Vector2 Direction { get; set; } public Vector2 Direction { get; set; }
protected override void OnDo() protected override void OnDo()
{ {
// 发送移动事件 // 发送移动事件
this.SendEvent(new PlayerMoveEvent { Direction = Direction }); this.SendEvent(new PlayerMoveEvent { Direction = Direction });
} }
} }
public class AttackCommand : AbstractCommand public class AttackCommand : AbstractCommand
{ {
public Vector2 TargetPosition { get; set; } public Vector2 TargetPosition { get; set; }
protected override void OnDo() protected override void OnDo()
{ {
// 发送攻击事件 // 发送攻击事件
this.SendEvent(new PlayerAttackEvent { TargetPosition = TargetPosition }); this.SendEvent(new PlayerAttackEvent { TargetPosition = TargetPosition });
} }
} }
``` ```
## 7. 运行应用 ## 7. 运行应用
现在让我们运行这个简单的应用: 现在让我们运行这个简单的应用:
```csharp ```csharp
class Program class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
// 创建并初始化架构 // 创建并初始化架构
var architecture = new GameArchitecture(); var architecture = new GameArchitecture();
architecture.Initialize(); architecture.Initialize();
// 创建控制器 // 创建控制器
var gameController = new GameController(); var gameController = new GameController();
gameController.Initialize(); gameController.Initialize();
// 开始游戏 // 开始游戏
gameController.StartGame(); gameController.StartGame();
// 模拟玩家操作 // 模拟玩家操作
gameController.MovePlayer(new Vector2(1, 0)); gameController.MovePlayer(new Vector2(1, 0));
gameController.PlayerAttack(new Vector2(5, 5)); gameController.PlayerAttack(new Vector2(5, 5));
// 模拟玩家受伤 // 模拟玩家受伤
var playerModel = architecture.GetModel<PlayerModel>(); var playerModel = architecture.GetModel<PlayerModel>();
playerModel.Health.Value = 50; playerModel.Health.Value = 50;
// 模拟玩家死亡 // 模拟玩家死亡
playerModel.Health.Value = 0; playerModel.Health.Value = 0;
Console.WriteLine("Press any key to exit..."); Console.WriteLine("Press any key to exit...");
Console.ReadKey(); Console.ReadKey();
} }
} }
``` ```
## 8. 运行结果 ## 8. 运行结果
执行程序后,您应该看到类似以下输出: 执行程序后,您应该看到类似以下输出:
``` ```
Game started! Game started!
Game running: True Game running: True
Player moved to (1, 0) Player moved to (1, 0)
Player took 25 damage Player took 25 damage
Score: 10 Score: 10
Health: 50 Health: 50
Health: 0 Health: 0
Player died Player died
Game Over! Game Over!
Game running: False Game running: False
Press any key to exit... Press any key to exit...
``` ```
## 下一步 ## 下一步
这个简单的示例展示了 GFramework 的核心概念: 这个简单的示例展示了 GFramework 的核心概念:
1. **架构模式** - 清晰的分层结构 1. **架构模式** - 清晰的分层结构
2. **响应式数据** - BindableProperty 自动更新 2. **响应式数据** - BindableProperty 自动更新
3. **事件驱动** - 松耦合的组件通信 3. **事件驱动** - 松耦合的组件通信
4. **命令模式** - 封装用户操作 4. **命令模式** - 封装用户操作

File diff suppressed because it is too large Load Diff

View File

@ -1,403 +1,403 @@
# ContextAware 生成器 # ContextAware 生成器
> 自动实现 IContextAware 接口,提供架构上下文访问能力 > 自动实现 IContextAware 接口,提供架构上下文访问能力
## 概述 ## 概述
ContextAware 生成器为标记了 `[ContextAware]` 属性的类自动生成 `IContextAware` 接口实现,使类能够便捷地访问架构上下文( ContextAware 生成器为标记了 `[ContextAware]` 属性的类自动生成 `IContextAware` 接口实现,使类能够便捷地访问架构上下文(
`IArchitectureContext`)。这是 GFramework 中最常用的源码生成器之一,几乎所有需要与架构交互的组件都会使用它。 `IArchitectureContext`)。这是 GFramework 中最常用的源码生成器之一,几乎所有需要与架构交互的组件都会使用它。
### 核心功能 ### 核心功能
- **自动接口实现**:无需手动实现 `IContextAware` 接口的 `SetContext()``GetContext()` 方法 - **自动接口实现**:无需手动实现 `IContextAware` 接口的 `SetContext()``GetContext()` 方法
- **懒加载上下文**`Context` 属性在首次访问时自动初始化 - **懒加载上下文**`Context` 属性在首次访问时自动初始化
- **默认提供者**:使用 `GameContextProvider` 作为默认上下文提供者 - **默认提供者**:使用 `GameContextProvider` 作为默认上下文提供者
- **测试友好**:支持通过 `SetContextProvider()` 配置自定义上下文提供者 - **测试友好**:支持通过 `SetContextProvider()` 配置自定义上下文提供者
## 基础使用 ## 基础使用
### 标记类 ### 标记类
使用 `[ContextAware]` 属性标记需要访问架构上下文的类: 使用 `[ContextAware]` 属性标记需要访问架构上下文的类:
```csharp ```csharp
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
[ContextAware] [ContextAware]
public partial class PlayerController : IController public partial class PlayerController : IController
{ {
public void Initialize() public void Initialize()
{ {
// 使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口) // 使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口)
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
var combatSystem = this.GetSystem<CombatSystem>(); var combatSystem = this.GetSystem<CombatSystem>();
this.SendEvent(new PlayerInitializedEvent()); this.SendEvent(new PlayerInitializedEvent());
} }
public void Attack(Enemy target) public void Attack(Enemy target)
{ {
var damage = this.GetUtility<DamageCalculator>().Calculate(this, target); var damage = this.GetUtility<DamageCalculator>().Calculate(this, target);
this.SendCommand(new DealDamageCommand(target, damage)); this.SendCommand(new DealDamageCommand(target, damage));
} }
} }
``` ```
### 必要条件 ### 必要条件
标记的类必须满足以下条件: 标记的类必须满足以下条件:
1. **必须是 `partial` 类**:生成器需要生成部分类代码 1. **必须是 `partial` 类**:生成器需要生成部分类代码
2. **必须是 `class` 类型**:不能是 `struct``interface` 2. **必须是 `class` 类型**:不能是 `struct``interface`
```csharp ```csharp
// ✅ 正确 // ✅ 正确
[ContextAware] [ContextAware]
public partial class MyController { } public partial class MyController { }
// ❌ 错误:缺少 partial 关键字 // ❌ 错误:缺少 partial 关键字
[ContextAware] [ContextAware]
public class MyController { } public class MyController { }
// ❌ 错误:不能用于 struct // ❌ 错误:不能用于 struct
[ContextAware] [ContextAware]
public partial struct MyStruct { } public partial struct MyStruct { }
``` ```
## 生成的代码 ## 生成的代码
编译器会为标记的类自动生成以下代码: 编译器会为标记的类自动生成以下代码:
```csharp ```csharp
// <auto-generated/> // <auto-generated/>
#nullable enable #nullable enable
namespace YourNamespace; namespace YourNamespace;
partial class PlayerController : global::GFramework.Core.Abstractions.Rule.IContextAware partial class PlayerController : global::GFramework.Core.Abstractions.Rule.IContextAware
{ {
private global::GFramework.Core.Abstractions.Architecture.IArchitectureContext? _context; private global::GFramework.Core.Abstractions.Architecture.IArchitectureContext? _context;
private static global::GFramework.Core.Abstractions.Architecture.IArchitectureContextProvider? _contextProvider; private static global::GFramework.Core.Abstractions.Architecture.IArchitectureContextProvider? _contextProvider;
/// <summary> /// <summary>
/// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider /// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider
/// </summary> /// </summary>
protected global::GFramework.Core.Abstractions.Architecture.IArchitectureContext Context protected global::GFramework.Core.Abstractions.Architecture.IArchitectureContext Context
{ {
get get
{ {
if (_context == null) if (_context == null)
{ {
_contextProvider ??= new global::GFramework.Core.Architecture.GameContextProvider(); _contextProvider ??= new global::GFramework.Core.Architecture.GameContextProvider();
_context = _contextProvider.GetContext(); _context = _contextProvider.GetContext();
} }
return _context; return _context;
} }
} }
/// <summary> /// <summary>
/// 配置上下文提供者(用于测试或多架构场景) /// 配置上下文提供者(用于测试或多架构场景)
/// </summary> /// </summary>
/// <param name="provider">上下文提供者实例</param> /// <param name="provider">上下文提供者实例</param>
public static void SetContextProvider(global::GFramework.Core.Abstractions.Architecture.IArchitectureContextProvider provider) public static void SetContextProvider(global::GFramework.Core.Abstractions.Architecture.IArchitectureContextProvider provider)
{ {
_contextProvider = provider; _contextProvider = provider;
} }
/// <summary> /// <summary>
/// 重置上下文提供者为默认值(用于测试清理) /// 重置上下文提供者为默认值(用于测试清理)
/// </summary> /// </summary>
public static void ResetContextProvider() public static void ResetContextProvider()
{ {
_contextProvider = null; _contextProvider = null;
} }
void global::GFramework.Core.Abstractions.Rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.Architecture.IArchitectureContext context) void global::GFramework.Core.Abstractions.Rule.IContextAware.SetContext(global::GFramework.Core.Abstractions.Architecture.IArchitectureContext context)
{ {
_context = context; _context = context;
} }
global::GFramework.Core.Abstractions.Architecture.IArchitectureContext global::GFramework.Core.Abstractions.Rule.IContextAware.GetContext() global::GFramework.Core.Abstractions.Architecture.IArchitectureContext global::GFramework.Core.Abstractions.Rule.IContextAware.GetContext()
{ {
return Context; return Context;
} }
} }
``` ```
### 代码解析 ### 代码解析
生成的代码包含以下关键部分: 生成的代码包含以下关键部分:
1. **私有字段** 1. **私有字段**
- `_context`:缓存的上下文实例 - `_context`:缓存的上下文实例
- `_contextProvider`:静态上下文提供者(所有实例共享) - `_contextProvider`:静态上下文提供者(所有实例共享)
2. **Context 属性** 2. **Context 属性**
- `protected` 访问级别,子类可访问 - `protected` 访问级别,子类可访问
- 懒加载:首次访问时自动初始化 - 懒加载:首次访问时自动初始化
- 使用 `GameContextProvider` 作为默认提供者 - 使用 `GameContextProvider` 作为默认提供者
3. **配置方法** 3. **配置方法**
- `SetContextProvider()`:设置自定义上下文提供者 - `SetContextProvider()`:设置自定义上下文提供者
- `ResetContextProvider()`:重置为默认提供者 - `ResetContextProvider()`:重置为默认提供者
4. **显式接口实现** 4. **显式接口实现**
- `IContextAware.SetContext()`:允许外部设置上下文 - `IContextAware.SetContext()`:允许外部设置上下文
- `IContextAware.GetContext()`:返回当前上下文 - `IContextAware.GetContext()`:返回当前上下文
## 配置上下文提供者 ## 配置上下文提供者
### 测试场景 ### 测试场景
在单元测试中,通常需要使用自定义的上下文提供者: 在单元测试中,通常需要使用自定义的上下文提供者:
```csharp ```csharp
[Test] [Test]
public async Task TestPlayerController() public async Task TestPlayerController()
{ {
// 创建测试架构 // 创建测试架构
var testArchitecture = new TestArchitecture(); var testArchitecture = new TestArchitecture();
await testArchitecture.InitAsync(); await testArchitecture.InitAsync();
// 配置自定义上下文提供者 // 配置自定义上下文提供者
PlayerController.SetContextProvider(new TestContextProvider(testArchitecture)); PlayerController.SetContextProvider(new TestContextProvider(testArchitecture));
try try
{ {
// 测试代码 // 测试代码
var controller = new PlayerController(); var controller = new PlayerController();
controller.Initialize(); controller.Initialize();
// 验证... // 验证...
} }
finally finally
{ {
// 清理:重置上下文提供者 // 清理:重置上下文提供者
PlayerController.ResetContextProvider(); PlayerController.ResetContextProvider();
} }
} }
``` ```
### 多架构场景 ### 多架构场景
在某些高级场景中,可能需要同时运行多个架构实例: 在某些高级场景中,可能需要同时运行多个架构实例:
```csharp ```csharp
public class MultiArchitectureManager public class MultiArchitectureManager
{ {
private readonly Dictionary<string, IArchitecture> _architectures = new(); private readonly Dictionary<string, IArchitecture> _architectures = new();
public void SwitchToArchitecture(string name) public void SwitchToArchitecture(string name)
{ {
var architecture = _architectures[name]; var architecture = _architectures[name];
var provider = new ScopedContextProvider(architecture); var provider = new ScopedContextProvider(architecture);
// 为所有使用 [ContextAware] 的类切换上下文 // 为所有使用 [ContextAware] 的类切换上下文
PlayerController.SetContextProvider(provider); PlayerController.SetContextProvider(provider);
EnemyController.SetContextProvider(provider); EnemyController.SetContextProvider(provider);
// ... // ...
} }
} }
``` ```
## 使用场景 ## 使用场景
### 何时使用 [ContextAware] ### 何时使用 [ContextAware]
推荐在以下场景使用 `[ContextAware]` 属性: 推荐在以下场景使用 `[ContextAware]` 属性:
1. **Controller 层**:需要协调多个 Model/System 的控制器 1. **Controller 层**:需要协调多个 Model/System 的控制器
2. **Command/Query 实现**:需要访问架构服务的命令或查询 2. **Command/Query 实现**:需要访问架构服务的命令或查询
3. **自定义组件**:不继承框架基类但需要上下文访问的组件 3. **自定义组件**:不继承框架基类但需要上下文访问的组件
```csharp ```csharp
[ContextAware] [ContextAware]
public partial class GameFlowController : IController public partial class GameFlowController : IController
{ {
public async Task StartGame() public async Task StartGame()
{ {
var saveSystem = this.GetSystem<SaveSystem>(); var saveSystem = this.GetSystem<SaveSystem>();
var uiSystem = this.GetSystem<UISystem>(); var uiSystem = this.GetSystem<UISystem>();
await saveSystem.LoadAsync(); await saveSystem.LoadAsync();
await uiSystem.ShowMainMenuAsync(); await uiSystem.ShowMainMenuAsync();
} }
} }
``` ```
### 与 IController 配合使用 ### 与 IController 配合使用
在 Godot 项目中,控制器通常同时实现 `IController` 和使用 `[ContextAware]` 在 Godot 项目中,控制器通常同时实现 `IController` 和使用 `[ContextAware]`
```csharp ```csharp
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[ContextAware] [ContextAware]
public partial class PlayerController : Node, IController public partial class PlayerController : Node, IController
{ {
public override void _Ready() public override void _Ready()
{ {
// 使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口) // 使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口)
var playerModel = this.GetModel<PlayerModel>(); var playerModel = this.GetModel<PlayerModel>();
var combatSystem = this.GetSystem<CombatSystem>(); var combatSystem = this.GetSystem<CombatSystem>();
} }
} }
``` ```
**说明** **说明**
- `IController` 是标记接口,标识这是一个控制器 - `IController` 是标记接口,标识这是一个控制器
- `[ContextAware]` 提供架构访问能力 - `[ContextAware]` 提供架构访问能力
- 两者配合使用是推荐的模式 - 两者配合使用是推荐的模式
### 何时继承 ContextAwareBase ### 何时继承 ContextAwareBase
如果类需要更多框架功能(如生命周期管理),应继承 `ContextAwareBase` 如果类需要更多框架功能(如生命周期管理),应继承 `ContextAwareBase`
```csharp ```csharp
// 推荐:需要生命周期管理时继承基类 // 推荐:需要生命周期管理时继承基类
public class PlayerModel : AbstractModel public class PlayerModel : AbstractModel
{ {
// AbstractModel 已经继承了 ContextAwareBase // AbstractModel 已经继承了 ContextAwareBase
protected override void OnInit() protected override void OnInit()
{ {
var config = this.GetUtility<ConfigLoader>().Load<PlayerConfig>(); var config = this.GetUtility<ConfigLoader>().Load<PlayerConfig>();
} }
} }
// 推荐:简单组件使用属性 // 推荐:简单组件使用属性
[ContextAware] [ContextAware]
public partial class SimpleHelper public partial class SimpleHelper
{ {
public void DoSomething() public void DoSomething()
{ {
this.SendEvent(new SomethingHappenedEvent()); this.SendEvent(new SomethingHappenedEvent());
} }
} }
``` ```
## 与 IContextAware 接口的关系 ## 与 IContextAware 接口的关系
生成的代码实现了 `IContextAware` 接口: 生成的代码实现了 `IContextAware` 接口:
```csharp ```csharp
namespace GFramework.Core.Abstractions.Rule; namespace GFramework.Core.Abstractions.Rule;
public interface IContextAware public interface IContextAware
{ {
void SetContext(IArchitectureContext context); void SetContext(IArchitectureContext context);
IArchitectureContext GetContext(); IArchitectureContext GetContext();
} }
``` ```
这意味着标记了 `[ContextAware]` 的类可以: 这意味着标记了 `[ContextAware]` 的类可以:
1. **被架构自动注入上下文**:实现 `IContextAware` 的类在注册到架构时会自动调用 `SetContext()` 1. **被架构自动注入上下文**:实现 `IContextAware` 的类在注册到架构时会自动调用 `SetContext()`
2. **参与依赖注入**:可以作为 `IContextAware` 类型注入到其他组件 2. **参与依赖注入**:可以作为 `IContextAware` 类型注入到其他组件
3. **支持上下文传递**:可以通过 `GetContext()` 将上下文传递给其他组件 3. **支持上下文传递**:可以通过 `GetContext()` 将上下文传递给其他组件
## 最佳实践 ## 最佳实践
### 1. 始终使用 partial 关键字 ### 1. 始终使用 partial 关键字
```csharp ```csharp
// ✅ 正确 // ✅ 正确
[ContextAware] [ContextAware]
public partial class MyController { } public partial class MyController { }
// ❌ 错误:编译器会报错 // ❌ 错误:编译器会报错
[ContextAware] [ContextAware]
public class MyController { } public class MyController { }
``` ```
### 2. 在测试中清理上下文提供者 ### 2. 在测试中清理上下文提供者
```csharp ```csharp
[TearDown] [TearDown]
public void TearDown() public void TearDown()
{ {
// 避免测试之间的状态污染 // 避免测试之间的状态污染
PlayerController.ResetContextProvider(); PlayerController.ResetContextProvider();
EnemyController.ResetContextProvider(); EnemyController.ResetContextProvider();
} }
``` ```
### 3. 避免在构造函数中访问 Context ### 3. 避免在构造函数中访问 Context
```csharp ```csharp
[ContextAware] [ContextAware]
public partial class MyController public partial class MyController
{ {
// ❌ 错误:构造函数执行时上下文可能未初始化 // ❌ 错误:构造函数执行时上下文可能未初始化
public MyController() public MyController()
{ {
var model = this.GetModel<SomeModel>(); // 可能为 null var model = this.GetModel<SomeModel>(); // 可能为 null
} }
// ✅ 正确:在初始化方法中访问 // ✅ 正确:在初始化方法中访问
public void Initialize() public void Initialize()
{ {
var model = this.GetModel<SomeModel>(); // 安全 var model = this.GetModel<SomeModel>(); // 安全
} }
} }
``` ```
### 4. 优先使用 Context 属性而非接口方法 ### 4. 优先使用 Context 属性而非接口方法
```csharp ```csharp
[ContextAware] [ContextAware]
public partial class MyController public partial class MyController
{ {
public void DoSomething() public void DoSomething()
{ {
// ✅ 推荐:使用扩展方法 // ✅ 推荐:使用扩展方法
var model = this.GetModel<SomeModel>(); var model = this.GetModel<SomeModel>();
// ❌ 不推荐:显式调用接口方法 // ❌ 不推荐:显式调用接口方法
var context = ((IContextAware)this).GetContext(); var context = ((IContextAware)this).GetContext();
var model2 = context.GetModel<SomeModel>(); var model2 = context.GetModel<SomeModel>();
} }
} }
``` ```
## 诊断信息 ## 诊断信息
生成器会在以下情况报告编译错误: 生成器会在以下情况报告编译错误:
### GFSG001: 类必须是 partial ### GFSG001: 类必须是 partial
```csharp ```csharp
[ContextAware] [ContextAware]
public class MyController { } // 错误:缺少 partial 关键字 public class MyController { } // 错误:缺少 partial 关键字
``` ```
**解决方案**:添加 `partial` 关键字 **解决方案**:添加 `partial` 关键字
```csharp ```csharp
[ContextAware] [ContextAware]
public partial class MyController { } // ✅ 正确 public partial class MyController { } // ✅ 正确
``` ```
### GFSG002: ContextAware 只能用于类 ### GFSG002: ContextAware 只能用于类
```csharp ```csharp
[ContextAware] [ContextAware]
public partial struct MyStruct { } // 错误:不能用于 struct public partial struct MyStruct { } // 错误:不能用于 struct
``` ```
**解决方案**:将 `struct` 改为 `class` **解决方案**:将 `struct` 改为 `class`
```csharp ```csharp
[ContextAware] [ContextAware]
public partial class MyClass { } // ✅ 正确 public partial class MyClass { } // ✅ 正确
``` ```
## 相关文档 ## 相关文档
- [Source Generators 概述](./index) - [Source Generators 概述](./index)
- [架构上下文](../core/context) - [架构上下文](../core/context)
- [IContextAware 接口](../core/rule) - [IContextAware 接口](../core/rule)
- [日志生成器](./logging-generator) - [日志生成器](./logging-generator)

File diff suppressed because it is too large Load Diff

View File

@ -1,341 +1,341 @@
# 日志生成器 # 日志生成器
> GFramework.SourceGenerators 自动生成日志代码,减少样板代码 > GFramework.SourceGenerators 自动生成日志代码,减少样板代码
## 概述 ## 概述
日志生成器是一个 Source Generator它会自动为标记了 `[Log]` 特性的类生成 ILogger 字段。这消除了手动编写日志字段的需要,让开发者专注于业务逻辑。 日志生成器是一个 Source Generator它会自动为标记了 `[Log]` 特性的类生成 ILogger 字段。这消除了手动编写日志字段的需要,让开发者专注于业务逻辑。
## 基本用法 ## 基本用法
### 标记类 ### 标记类
```csharp ```csharp
using GFramework.SourceGenerators.Abstractions.Logging; using GFramework.SourceGenerators.Abstractions.Logging;
[Log] [Log]
public partial class MyService public partial class MyService
{ {
public void DoSomething() public void DoSomething()
{ {
// 自动生成的 Logger 字段可直接使用 // 自动生成的 Logger 字段可直接使用
Logger.Info("执行操作"); Logger.Info("执行操作");
} }
} }
``` ```
### 生成代码 ### 生成代码
上面的代码会被编译时转换为: 上面的代码会被编译时转换为:
```csharp ```csharp
// <auto-generated/> // <auto-generated/>
public partial class MyService public partial class MyService
{ {
private static readonly ILogger Logger = private static readonly ILogger Logger =
LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.MyService"); LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.MyService");
} }
``` ```
**注意**:生成器只生成 ILogger 字段不生成日志方法。日志方法Info、Debug、Error 等)来自 ILogger 接口本身。 **注意**:生成器只生成 ILogger 字段不生成日志方法。日志方法Info、Debug、Error 等)来自 ILogger 接口本身。
## 日志级别 ## 日志级别
生成的 Logger 字段支持 ILogger 接口的所有方法: 生成的 Logger 字段支持 ILogger 接口的所有方法:
```csharp ```csharp
[Log] [Log]
public partial class MyClass public partial class MyClass
{ {
public void Example() public void Example()
{ {
// 调试信息 // 调试信息
Logger.Debug($"调试信息: {value}"); Logger.Debug($"调试信息: {value}");
// 普通信息 // 普通信息
Logger.Info("操作成功"); Logger.Info("操作成功");
// 警告 // 警告
Logger.Warning($"警告: {message}"); Logger.Warning($"警告: {message}");
// 错误 // 错误
Logger.Error($"错误: {ex.Message}"); Logger.Error($"错误: {ex.Message}");
// 严重错误 // 严重错误
Logger.Critical("系统故障"); Logger.Critical("系统故障");
} }
} }
``` ```
## 自定义日志类别 ## 自定义日志类别
```csharp ```csharp
[Log("Gameplay")] [Log("Gameplay")]
public partial class GameplaySystem public partial class GameplaySystem
{ {
// 日志会标记为 Gameplay 类别 // 日志会标记为 Gameplay 类别
public void Update() public void Update()
{ {
Logger.Info("游戏逻辑更新"); Logger.Info("游戏逻辑更新");
} }
} }
``` ```
## 配置选项 ## 配置选项
### 自定义字段名称 ### 自定义字段名称
```csharp ```csharp
[Log(FieldName = "_customLogger")] [Log(FieldName = "_customLogger")]
public partial class MyClass public partial class MyClass
{ {
public void DoSomething() public void DoSomething()
{ {
// 使用自定义字段名 // 使用自定义字段名
_customLogger.Info("使用自定义日志器"); _customLogger.Info("使用自定义日志器");
} }
} }
``` ```
### 非静态字段 ### 非静态字段
```csharp ```csharp
[Log(IsStatic = false)] [Log(IsStatic = false)]
public partial class InstanceLogger public partial class InstanceLogger
{ {
// 生成实例字段而非静态字段 // 生成实例字段而非静态字段
public void LogMessage() public void LogMessage()
{ {
Logger.Info("实例日志"); Logger.Info("实例日志");
} }
} }
``` ```
### 访问修饰符 ### 访问修饰符
```csharp ```csharp
[Log(AccessModifier = "protected")] [Log(AccessModifier = "protected")]
public partial class ProtectedLogger public partial class ProtectedLogger
{ {
// 生成 protected 字段 // 生成 protected 字段
} }
``` ```
### 配置选项说明 ### 配置选项说明
| 参数 | 类型 | 默认值 | 说明 | | 参数 | 类型 | 默认值 | 说明 |
|----------------|---------|-----------|---------------------------------| |----------------|---------|-----------|---------------------------------|
| Name | string? | null | 日志分类名称(默认使用类名) | | Name | string? | null | 日志分类名称(默认使用类名) |
| FieldName | string | "Logger" | 生成的字段名称 | | FieldName | string | "Logger" | 生成的字段名称 |
| IsStatic | bool | true | 是否生成静态字段 | | IsStatic | bool | true | 是否生成静态字段 |
| AccessModifier | string | "private" | 访问修饰符private/protected/public | | AccessModifier | string | "private" | 访问修饰符private/protected/public |
## 与其他模块集成 ## 与其他模块集成
### 与 Godot 集成 ### 与 Godot 集成
```csharp ```csharp
[Log] [Log]
[ContextAware] [ContextAware]
public partial class GodotController : Node public partial class GodotController : Node
{ {
public override void _Ready() public override void _Ready()
{ {
Logger.Info("控制器已准备就绪"); Logger.Info("控制器已准备就绪");
} }
} }
``` ```
### 与架构集成 ### 与架构集成
```csharp ```csharp
[Log] [Log]
public partial class MySystem : AbstractSystem public partial class MySystem : AbstractSystem
{ {
protected override void OnInit() protected override void OnInit()
{ {
Logger.Info("系统初始化"); Logger.Info("系统初始化");
} }
} }
``` ```
## 实际应用示例 ## 实际应用示例
### 游戏控制器 ### 游戏控制器
```csharp ```csharp
using GFramework.Core.Abstractions.Controller; using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Logging; using GFramework.SourceGenerators.Abstractions.Logging;
using GFramework.SourceGenerators.Abstractions.Rule; using GFramework.SourceGenerators.Abstractions.Rule;
[Log] [Log]
[ContextAware] [ContextAware]
public partial class PlayerController : IController public partial class PlayerController : IController
{ {
public void HandleInput(string action) public void HandleInput(string action)
{ {
Logger.Debug($"处理输入: {action}"); Logger.Debug($"处理输入: {action}");
switch (action) switch (action)
{ {
case "jump": case "jump":
Logger.Info("玩家跳跃"); Logger.Info("玩家跳跃");
Jump(); Jump();
break; break;
case "attack": case "attack":
Logger.Info("玩家攻击"); Logger.Info("玩家攻击");
Attack(); Attack();
break; break;
default: default:
Logger.Warning($"未知操作: {action}"); Logger.Warning($"未知操作: {action}");
break; break;
} }
} }
private void Jump() private void Jump()
{ {
try try
{ {
// 跳跃逻辑 // 跳跃逻辑
Logger.Debug("跳跃执行成功"); Logger.Debug("跳跃执行成功");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error($"跳跃失败: {ex.Message}"); Logger.Error($"跳跃失败: {ex.Message}");
} }
} }
} }
``` ```
### 数据处理服务 ### 数据处理服务
```csharp ```csharp
[Log("DataService")] [Log("DataService")]
public partial class DataProcessor public partial class DataProcessor
{ {
public void ProcessData(string data) public void ProcessData(string data)
{ {
Logger.Info($"开始处理数据,长度: {data.Length}"); Logger.Info($"开始处理数据,长度: {data.Length}");
if (string.IsNullOrEmpty(data)) if (string.IsNullOrEmpty(data))
{ {
Logger.Warning("数据为空,跳过处理"); Logger.Warning("数据为空,跳过处理");
return; return;
} }
try try
{ {
// 处理逻辑 // 处理逻辑
Logger.Debug("数据处理中..."); Logger.Debug("数据处理中...");
// ... // ...
Logger.Info("数据处理完成"); Logger.Info("数据处理完成");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error($"数据处理失败: {ex.Message}"); Logger.Error($"数据处理失败: {ex.Message}");
throw; throw;
} }
} }
} }
``` ```
## 最佳实践 ## 最佳实践
### 1. 合理使用日志级别 ### 1. 合理使用日志级别
```csharp ```csharp
[Log] [Log]
public partial class BestPracticeExample public partial class BestPracticeExample
{ {
public void ProcessRequest() public void ProcessRequest()
{ {
// Debug: 详细的调试信息 // Debug: 详细的调试信息
Logger.Debug("开始处理请求"); Logger.Debug("开始处理请求");
// Info: 重要的业务流程信息 // Info: 重要的业务流程信息
Logger.Info("请求处理成功"); Logger.Info("请求处理成功");
// Warning: 可恢复的异常情况 // Warning: 可恢复的异常情况
Logger.Warning("缓存未命中,使用默认值"); Logger.Warning("缓存未命中,使用默认值");
// Error: 错误但不影响系统运行 // Error: 错误但不影响系统运行
Logger.Error("处理失败,将重试"); Logger.Error("处理失败,将重试");
// Critical: 严重错误,可能导致系统崩溃 // Critical: 严重错误,可能导致系统崩溃
Logger.Critical("数据库连接失败"); Logger.Critical("数据库连接失败");
} }
} }
``` ```
### 2. 避免过度日志 ### 2. 避免过度日志
```csharp ```csharp
[Log] [Log]
public partial class PerformanceExample public partial class PerformanceExample
{ {
private int _frameCount = 0; private int _frameCount = 0;
public void Update() public void Update()
{ {
// 好的做法:定期记录 // 好的做法:定期记录
if (_frameCount % 1000 == 0) if (_frameCount % 1000 == 0)
{ {
Logger.Debug($"已运行 {_frameCount} 帧"); Logger.Debug($"已运行 {_frameCount} 帧");
} }
_frameCount++; _frameCount++;
// 避免:每帧都记录 // 避免:每帧都记录
// Logger.Debug($"帧 {_frameCount}"); // ❌ 太频繁 // Logger.Debug($"帧 {_frameCount}"); // ❌ 太频繁
} }
} }
``` ```
### 3. 结构化日志信息 ### 3. 结构化日志信息
```csharp ```csharp
[Log] [Log]
public partial class StructuredLogging public partial class StructuredLogging
{ {
public void ProcessUser(int userId, string action) public void ProcessUser(int userId, string action)
{ {
// 好的做法:包含上下文信息 // 好的做法:包含上下文信息
Logger.Info($"用户操作 [UserId={userId}, Action={action}]"); Logger.Info($"用户操作 [UserId={userId}, Action={action}]");
// 避免:信息不完整 // 避免:信息不完整
// Logger.Info("用户操作"); // ❌ 缺少上下文 // Logger.Info("用户操作"); // ❌ 缺少上下文
} }
} }
``` ```
## 常见问题 ## 常见问题
### Q: 为什么需要 partial 关键字? ### Q: 为什么需要 partial 关键字?
**A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。 **A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。
### Q: 可以在静态类中使用吗? ### Q: 可以在静态类中使用吗?
**A**: 可以,生成器会自动生成静态字段: **A**: 可以,生成器会自动生成静态字段:
```csharp ```csharp
[Log] [Log]
public static partial class StaticHelper public static partial class StaticHelper
{ {
public static void DoSomething() public static void DoSomething()
{ {
Logger.Info("静态方法日志"); Logger.Info("静态方法日志");
} }
} }
``` ```
### Q: 如何自定义日志工厂? ### Q: 如何自定义日志工厂?
**A**: 通过配置 `LoggerFactoryResolver.Provider` 来自定义日志工厂实现。 **A**: 通过配置 `LoggerFactoryResolver.Provider` 来自定义日志工厂实现。
--- ---
**相关文档** **相关文档**
- [Source Generators 概述](./index) - [Source Generators 概述](./index)
- [枚举扩展生成器](./enum-generator) - [枚举扩展生成器](./enum-generator)
- [ContextAware 生成器](./context-aware-generator) - [ContextAware 生成器](./context-aware-generator)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,456 +1,456 @@
#!/usr/bin/env pwsh #!/usr/bin/env pwsh
<# <#
.SYNOPSIS .SYNOPSIS
GFramework 命名空间重构脚本 - 将所有文件夹和命名空间从小写改为 PascalCase GFramework 命名空间重构脚本 - 将所有文件夹和命名空间从小写改为 PascalCase
.DESCRIPTION .DESCRIPTION
此脚本执行以下操作 此脚本执行以下操作
1. 重命名文件夹使用 git mv 保留历史 1. 重命名文件夹使用 git mv 保留历史
2. 更新所有 C# 文件中的命名空间声明和 using 语句 2. 更新所有 C# 文件中的命名空间声明和 using 语句
3. 更新文档中的命名空间引用 3. 更新文档中的命名空间引用
4. 验证更改的完整性 4. 验证更改的完整性
.PARAMETER Phase .PARAMETER Phase
指定要执行的阶段 指定要执行的阶段
- 1: 文件夹重命名 - 1: 文件夹重命名
- 2: 命名空间更新 - 2: 命名空间更新
- 3: 文档更新 - 3: 文档更新
- 4: 验证 - 4: 验证
- All: 执行所有阶段默认 - All: 执行所有阶段默认
.PARAMETER DryRun .PARAMETER DryRun
干运行模式只显示将要执行的操作不实际执行 干运行模式只显示将要执行的操作不实际执行
.PARAMETER SkipTests .PARAMETER SkipTests
跳过测试验证 跳过测试验证
.EXAMPLE .EXAMPLE
./refactor-namespaces.ps1 -Phase 1 ./refactor-namespaces.ps1 -Phase 1
./refactor-namespaces.ps1 -DryRun ./refactor-namespaces.ps1 -DryRun
./refactor-namespaces.ps1 -Phase All -SkipTests ./refactor-namespaces.ps1 -Phase All -SkipTests
#> #>
param( param(
[Parameter()] [Parameter()]
[ValidateSet("1", "2", "3", "4", "All")] [ValidateSet("1", "2", "3", "4", "All")]
[string]$Phase = "All", [string]$Phase = "All",
[Parameter()] [Parameter()]
[switch]$DryRun, [switch]$DryRun,
[Parameter()] [Parameter()]
[switch]$SkipTests [switch]$SkipTests
) )
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$rootDir = Split-Path -Parent $scriptDir $rootDir = Split-Path -Parent $scriptDir
$mappingFile = Join-Path $scriptDir "folder-mappings.json" $mappingFile = Join-Path $scriptDir "folder-mappings.json"
# 颜色输出函数 # 颜色输出函数
function Write-ColorOutput { function Write-ColorOutput {
param( param(
[string]$Message, [string]$Message,
[string]$Color = "White" [string]$Color = "White"
) )
Write-Host $Message -ForegroundColor $Color Write-Host $Message -ForegroundColor $Color
} }
function Write-Success { param([string]$Message) Write-ColorOutput "$Message" "Green" } function Write-Success { param([string]$Message) Write-ColorOutput "$Message" "Green" }
function Write-Info { param([string]$Message) Write-ColorOutput " $Message" "Cyan" } function Write-Info { param([string]$Message) Write-ColorOutput " $Message" "Cyan" }
function Write-Warning { param([string]$Message) Write-ColorOutput "$Message" "Yellow" } function Write-Warning { param([string]$Message) Write-ColorOutput "$Message" "Yellow" }
function Write-Error { param([string]$Message) Write-ColorOutput "$Message" "Red" } function Write-Error { param([string]$Message) Write-ColorOutput "$Message" "Red" }
# 阶段 1: 文件夹重命名 # 阶段 1: 文件夹重命名
function Invoke-FolderRename { function Invoke-FolderRename {
Write-Info "=== 阶段 1: 文件夹重命名 ===" Write-Info "=== 阶段 1: 文件夹重命名 ==="
$config = Get-Content $mappingFile | ConvertFrom-Json $config = Get-Content $mappingFile | ConvertFrom-Json
$totalFolders = 0 $totalFolders = 0
foreach ($project in $config.projects) { foreach ($project in $config.projects) {
Write-Info "处理项目: $($project.name)" Write-Info "处理项目: $($project.name)"
$projectPath = Join-Path $rootDir $project.path $projectPath = Join-Path $rootDir $project.path
if (-not (Test-Path $projectPath)) { if (-not (Test-Path $projectPath)) {
Write-Warning "项目路径不存在: $projectPath" Write-Warning "项目路径不存在: $projectPath"
continue continue
} }
# 按深度排序(深度优先,避免路径冲突) # 按深度排序(深度优先,避免路径冲突)
$sortedFolders = $project.folders | Sort-Object { ($_.from -split '/').Count } -Descending $sortedFolders = $project.folders | Sort-Object { ($_.from -split '/').Count } -Descending
foreach ($folder in $sortedFolders) { foreach ($folder in $sortedFolders) {
$fromPath = Join-Path $projectPath $folder.from $fromPath = Join-Path $projectPath $folder.from
$toPath = Join-Path $projectPath $folder.to $toPath = Join-Path $projectPath $folder.to
if (-not (Test-Path $fromPath)) { if (-not (Test-Path $fromPath)) {
Write-Warning "源文件夹不存在: $fromPath" Write-Warning "源文件夹不存在: $fromPath"
continue continue
} }
if ($fromPath -eq $toPath) { if ($fromPath -eq $toPath) {
Write-Info "跳过(路径相同): $($folder.from)" Write-Info "跳过(路径相同): $($folder.from)"
continue continue
} }
# Windows 文件系统不区分大小写,需要两步重命名 # Windows 文件系统不区分大小写,需要两步重命名
$tempPath = "$fromPath`_temp" $tempPath = "$fromPath`_temp"
if ($DryRun) { if ($DryRun) {
Write-Info "[DRY RUN] git mv $fromPath $tempPath" Write-Info "[DRY RUN] git mv $fromPath $tempPath"
Write-Info "[DRY RUN] git mv $tempPath $toPath" Write-Info "[DRY RUN] git mv $tempPath $toPath"
} else { } else {
try { try {
Write-Info "重命名: $($folder.from) -> $($folder.to)" Write-Info "重命名: $($folder.from) -> $($folder.to)"
# 第一步:重命名为临时名称 # 第一步:重命名为临时名称
git mv $fromPath $tempPath git mv $fromPath $tempPath
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
throw "git mv 失败: $fromPath -> $tempPath" throw "git mv 失败: $fromPath -> $tempPath"
} }
# 第二步:重命名为目标名称 # 第二步:重命名为目标名称
git mv $tempPath $toPath git mv $tempPath $toPath
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
throw "git mv 失败: $tempPath -> $toPath" throw "git mv 失败: $tempPath -> $toPath"
} }
$totalFolders++ $totalFolders++
Write-Success "完成: $($folder.from) -> $($folder.to)" Write-Success "完成: $($folder.from) -> $($folder.to)"
} catch { } catch {
Write-Error "重命名失败: $_" Write-Error "重命名失败: $_"
throw throw
} }
} }
} }
if (-not $DryRun) { if (-not $DryRun) {
Write-Info "提交项目 $($project.name) 的文件夹重命名" Write-Info "提交项目 $($project.name) 的文件夹重命名"
git add -A git add -A
git commit -m "refactor($($project.name)): 重命名文件夹为 PascalCase" git commit -m "refactor($($project.name)): 重命名文件夹为 PascalCase"
} }
} }
Write-Success "阶段 1 完成: 共重命名 $totalFolders 个文件夹" Write-Success "阶段 1 完成: 共重命名 $totalFolders 个文件夹"
} }
# 阶段 2: 命名空间更新 # 阶段 2: 命名空间更新
function Invoke-NamespaceUpdate { function Invoke-NamespaceUpdate {
Write-Info "=== 阶段 2: 命名空间更新 ===" Write-Info "=== 阶段 2: 命名空间更新 ==="
$csFiles = Get-ChildItem -Path $rootDir -Filter "*.cs" -Recurse | $csFiles = Get-ChildItem -Path $rootDir -Filter "*.cs" -Recurse |
Where-Object { $_.FullName -notmatch "\\bin\\|\\obj\\|\\Generated\\" } Where-Object { $_.FullName -notmatch "\\bin\\|\\obj\\|\\Generated\\" }
Write-Info "找到 $($csFiles.Count) 个 C# 文件" Write-Info "找到 $($csFiles.Count) 个 C# 文件"
$updatedFiles = 0 $updatedFiles = 0
$totalReplacements = 0 $totalReplacements = 0
# 定义命名空间替换规则(按优先级排序,长的先匹配) # 定义命名空间替换规则(按优先级排序,长的先匹配)
$namespaceRules = @( $namespaceRules = @(
# CQRS 子命名空间 # CQRS 子命名空间
@{ Pattern = '\.cqrs\.notification\b'; Replacement = '.CQRS.Notification' } @{ Pattern = '\.cqrs\.notification\b'; Replacement = '.CQRS.Notification' }
@{ Pattern = '\.cqrs\.command\b'; Replacement = '.CQRS.Command' } @{ Pattern = '\.cqrs\.command\b'; Replacement = '.CQRS.Command' }
@{ Pattern = '\.cqrs\.request\b'; Replacement = '.CQRS.Request' } @{ Pattern = '\.cqrs\.request\b'; Replacement = '.CQRS.Request' }
@{ Pattern = '\.cqrs\.query\b'; Replacement = '.CQRS.Query' } @{ Pattern = '\.cqrs\.query\b'; Replacement = '.CQRS.Query' }
@{ Pattern = '\.cqrs\.behaviors\b'; Replacement = '.CQRS.Behaviors' } @{ Pattern = '\.cqrs\.behaviors\b'; Replacement = '.CQRS.Behaviors' }
@{ Pattern = '\.cqrs\b'; Replacement = '.CQRS' } @{ Pattern = '\.cqrs\b'; Replacement = '.CQRS' }
# 其他嵌套命名空间 # 其他嵌套命名空间
@{ Pattern = '\.coroutine\.instructions\b'; Replacement = '.Coroutine.Instructions' } @{ Pattern = '\.coroutine\.instructions\b'; Replacement = '.Coroutine.Instructions' }
@{ Pattern = '\.coroutine\.extensions\b'; Replacement = '.Coroutine.Extensions' } @{ Pattern = '\.coroutine\.extensions\b'; Replacement = '.Coroutine.Extensions' }
@{ Pattern = '\.coroutine\b'; Replacement = '.Coroutine' } @{ Pattern = '\.coroutine\b'; Replacement = '.Coroutine' }
@{ Pattern = '\.events\.filters\b'; Replacement = '.Events.Filters' } @{ Pattern = '\.events\.filters\b'; Replacement = '.Events.Filters' }
@{ Pattern = '\.events\b'; Replacement = '.Events' } @{ Pattern = '\.events\b'; Replacement = '.Events' }
@{ Pattern = '\.logging\.appenders\b'; Replacement = '.Logging.Appenders' } @{ Pattern = '\.logging\.appenders\b'; Replacement = '.Logging.Appenders' }
@{ Pattern = '\.logging\.filters\b'; Replacement = '.Logging.Filters' } @{ Pattern = '\.logging\.filters\b'; Replacement = '.Logging.Filters' }
@{ Pattern = '\.logging\.formatters\b'; Replacement = '.Logging.Formatters' } @{ Pattern = '\.logging\.formatters\b'; Replacement = '.Logging.Formatters' }
@{ Pattern = '\.logging\b'; Replacement = '.Logging' } @{ Pattern = '\.logging\b'; Replacement = '.Logging' }
@{ Pattern = '\.functional\.async\b'; Replacement = '.Functional.Async' } @{ Pattern = '\.functional\.async\b'; Replacement = '.Functional.Async' }
@{ Pattern = '\.functional\.control\b'; Replacement = '.Functional.Control' } @{ Pattern = '\.functional\.control\b'; Replacement = '.Functional.Control' }
@{ Pattern = '\.functional\.functions\b'; Replacement = '.Functional.Functions' } @{ Pattern = '\.functional\.functions\b'; Replacement = '.Functional.Functions' }
@{ Pattern = '\.functional\.pipe\b'; Replacement = '.Functional.Pipe' } @{ Pattern = '\.functional\.pipe\b'; Replacement = '.Functional.Pipe' }
@{ Pattern = '\.functional\.result\b'; Replacement = '.Functional.Result' } @{ Pattern = '\.functional\.result\b'; Replacement = '.Functional.Result' }
@{ Pattern = '\.functional\b'; Replacement = '.Functional' } @{ Pattern = '\.functional\b'; Replacement = '.Functional' }
@{ Pattern = '\.services\.modules\b'; Replacement = '.Services.Modules' } @{ Pattern = '\.services\.modules\b'; Replacement = '.Services.Modules' }
@{ Pattern = '\.services\b'; Replacement = '.Services' } @{ Pattern = '\.services\b'; Replacement = '.Services' }
# 单层命名空间 # 单层命名空间
@{ Pattern = '\.architecture\b'; Replacement = '.Architecture' } @{ Pattern = '\.architecture\b'; Replacement = '.Architecture' }
@{ Pattern = '\.bases\b'; Replacement = '.Bases' } @{ Pattern = '\.bases\b'; Replacement = '.Bases' }
@{ Pattern = '\.command\b'; Replacement = '.Command' } @{ Pattern = '\.command\b'; Replacement = '.Command' }
@{ Pattern = '\.configuration\b'; Replacement = '.Configuration' } @{ Pattern = '\.configuration\b'; Replacement = '.Configuration' }
@{ Pattern = '\.constants\b'; Replacement = '.Constants' } @{ Pattern = '\.constants\b'; Replacement = '.Constants' }
@{ Pattern = '\.data\b'; Replacement = '.Data' } @{ Pattern = '\.data\b'; Replacement = '.Data' }
@{ Pattern = '\.enums\b'; Replacement = '.Enums' } @{ Pattern = '\.enums\b'; Replacement = '.Enums' }
@{ Pattern = '\.environment\b'; Replacement = '.Environment' } @{ Pattern = '\.environment\b'; Replacement = '.Environment' }
@{ Pattern = '\.extensions\b'; Replacement = '.Extensions' } @{ Pattern = '\.extensions\b'; Replacement = '.Extensions' }
@{ Pattern = '\.internals\b'; Replacement = '.Internals' } @{ Pattern = '\.internals\b'; Replacement = '.Internals' }
@{ Pattern = '\.ioc\b'; Replacement = '.IoC' } @{ Pattern = '\.ioc\b'; Replacement = '.IoC' }
@{ Pattern = '\.lifecycle\b'; Replacement = '.Lifecycle' } @{ Pattern = '\.lifecycle\b'; Replacement = '.Lifecycle' }
@{ Pattern = '\.model\b'; Replacement = '.Model' } @{ Pattern = '\.model\b'; Replacement = '.Model' }
@{ Pattern = '\.pause\b'; Replacement = '.Pause' } @{ Pattern = '\.pause\b'; Replacement = '.Pause' }
@{ Pattern = '\.pool\b'; Replacement = '.Pool' } @{ Pattern = '\.pool\b'; Replacement = '.Pool' }
@{ Pattern = '\.properties\b'; Replacement = '.Properties' } @{ Pattern = '\.properties\b'; Replacement = '.Properties' }
@{ Pattern = '\.property\b'; Replacement = '.Property' } @{ Pattern = '\.property\b'; Replacement = '.Property' }
@{ Pattern = '\.query\b'; Replacement = '.Query' } @{ Pattern = '\.query\b'; Replacement = '.Query' }
@{ Pattern = '\.registries\b'; Replacement = '.Registries' } @{ Pattern = '\.registries\b'; Replacement = '.Registries' }
@{ Pattern = '\.resource\b'; Replacement = '.Resource' } @{ Pattern = '\.resource\b'; Replacement = '.Resource' }
@{ Pattern = '\.rule\b'; Replacement = '.Rule' } @{ Pattern = '\.rule\b'; Replacement = '.Rule' }
@{ Pattern = '\.serializer\b'; Replacement = '.Serializer' } @{ Pattern = '\.serializer\b'; Replacement = '.Serializer' }
@{ Pattern = '\.state\b'; Replacement = '.State' } @{ Pattern = '\.state\b'; Replacement = '.State' }
@{ Pattern = '\.storage\b'; Replacement = '.Storage' } @{ Pattern = '\.storage\b'; Replacement = '.Storage' }
@{ Pattern = '\.system\b'; Replacement = '.System' } @{ Pattern = '\.system\b'; Replacement = '.System' }
@{ Pattern = '\.time\b'; Replacement = '.Time' } @{ Pattern = '\.time\b'; Replacement = '.Time' }
@{ Pattern = '\.utility\b'; Replacement = '.Utility' } @{ Pattern = '\.utility\b'; Replacement = '.Utility' }
@{ Pattern = '\.versioning\b'; Replacement = '.Versioning' } @{ Pattern = '\.versioning\b'; Replacement = '.Versioning' }
) )
foreach ($file in $csFiles) { foreach ($file in $csFiles) {
$content = Get-Content $file.FullName -Raw $content = Get-Content $file.FullName -Raw
$originalContent = $content $originalContent = $content
$fileReplacements = 0 $fileReplacements = 0
foreach ($rule in $namespaceRules) { foreach ($rule in $namespaceRules) {
$matches = [regex]::Matches($content, $rule.Pattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) $matches = [regex]::Matches($content, $rule.Pattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
if ($matches.Count -gt 0) { if ($matches.Count -gt 0) {
$content = [regex]::Replace($content, $rule.Pattern, $rule.Replacement, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) $content = [regex]::Replace($content, $rule.Pattern, $rule.Replacement, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
$fileReplacements += $matches.Count $fileReplacements += $matches.Count
} }
} }
if ($content -ne $originalContent) { if ($content -ne $originalContent) {
if ($DryRun) { if ($DryRun) {
Write-Info "[DRY RUN] 更新文件: $($file.FullName) ($fileReplacements 处替换)" Write-Info "[DRY RUN] 更新文件: $($file.FullName) ($fileReplacements 处替换)"
} else { } else {
Set-Content -Path $file.FullName -Value $content -NoNewline Set-Content -Path $file.FullName -Value $content -NoNewline
$updatedFiles++ $updatedFiles++
$totalReplacements += $fileReplacements $totalReplacements += $fileReplacements
Write-Info "更新: $($file.Name) ($fileReplacements 处替换)" Write-Info "更新: $($file.Name) ($fileReplacements 处替换)"
} }
} }
} }
if (-not $DryRun) { if (-not $DryRun) {
Write-Info "提交命名空间更新" Write-Info "提交命名空间更新"
git add -A git add -A
git commit -m "refactor: 更新所有命名空间为 PascalCase" git commit -m "refactor: 更新所有命名空间为 PascalCase"
} }
Write-Success "阶段 2 完成: 更新了 $updatedFiles 个文件,共 $totalReplacements 处替换" Write-Success "阶段 2 完成: 更新了 $updatedFiles 个文件,共 $totalReplacements 处替换"
} }
# 阶段 3: 文档更新 # 阶段 3: 文档更新
function Invoke-DocumentationUpdate { function Invoke-DocumentationUpdate {
Write-Info "=== 阶段 3: 文档更新 ===" Write-Info "=== 阶段 3: 文档更新 ==="
$mdFiles = Get-ChildItem -Path $rootDir -Filter "*.md" -Recurse | $mdFiles = Get-ChildItem -Path $rootDir -Filter "*.md" -Recurse |
Where-Object { $_.FullName -notmatch "\\node_modules\\|\\bin\\|\\obj\\" } Where-Object { $_.FullName -notmatch "\\node_modules\\|\\bin\\|\\obj\\" }
Write-Info "找到 $($mdFiles.Count) 个 Markdown 文件" Write-Info "找到 $($mdFiles.Count) 个 Markdown 文件"
$updatedFiles = 0 $updatedFiles = 0
$totalReplacements = 0 $totalReplacements = 0
# 使用与阶段 2 相同的替换规则 # 使用与阶段 2 相同的替换规则
$namespaceRules = @( $namespaceRules = @(
@{ Pattern = '\.cqrs\.notification\b'; Replacement = '.CQRS.Notification' } @{ Pattern = '\.cqrs\.notification\b'; Replacement = '.CQRS.Notification' }
@{ Pattern = '\.cqrs\.command\b'; Replacement = '.CQRS.Command' } @{ Pattern = '\.cqrs\.command\b'; Replacement = '.CQRS.Command' }
@{ Pattern = '\.cqrs\.request\b'; Replacement = '.CQRS.Request' } @{ Pattern = '\.cqrs\.request\b'; Replacement = '.CQRS.Request' }
@{ Pattern = '\.cqrs\.query\b'; Replacement = '.CQRS.Query' } @{ Pattern = '\.cqrs\.query\b'; Replacement = '.CQRS.Query' }
@{ Pattern = '\.cqrs\.behaviors\b'; Replacement = '.CQRS.Behaviors' } @{ Pattern = '\.cqrs\.behaviors\b'; Replacement = '.CQRS.Behaviors' }
@{ Pattern = '\.cqrs\b'; Replacement = '.CQRS' } @{ Pattern = '\.cqrs\b'; Replacement = '.CQRS' }
@{ Pattern = '\.coroutine\.instructions\b'; Replacement = '.Coroutine.Instructions' } @{ Pattern = '\.coroutine\.instructions\b'; Replacement = '.Coroutine.Instructions' }
@{ Pattern = '\.coroutine\.extensions\b'; Replacement = '.Coroutine.Extensions' } @{ Pattern = '\.coroutine\.extensions\b'; Replacement = '.Coroutine.Extensions' }
@{ Pattern = '\.coroutine\b'; Replacement = '.Coroutine' } @{ Pattern = '\.coroutine\b'; Replacement = '.Coroutine' }
@{ Pattern = '\.events\.filters\b'; Replacement = '.Events.Filters' } @{ Pattern = '\.events\.filters\b'; Replacement = '.Events.Filters' }
@{ Pattern = '\.events\b'; Replacement = '.Events' } @{ Pattern = '\.events\b'; Replacement = '.Events' }
@{ Pattern = '\.logging\.appenders\b'; Replacement = '.Logging.Appenders' } @{ Pattern = '\.logging\.appenders\b'; Replacement = '.Logging.Appenders' }
@{ Pattern = '\.logging\.filters\b'; Replacement = '.Logging.Filters' } @{ Pattern = '\.logging\.filters\b'; Replacement = '.Logging.Filters' }
@{ Pattern = '\.logging\.formatters\b'; Replacement = '.Logging.Formatters' } @{ Pattern = '\.logging\.formatters\b'; Replacement = '.Logging.Formatters' }
@{ Pattern = '\.logging\b'; Replacement = '.Logging' } @{ Pattern = '\.logging\b'; Replacement = '.Logging' }
@{ Pattern = '\.functional\.async\b'; Replacement = '.Functional.Async' } @{ Pattern = '\.functional\.async\b'; Replacement = '.Functional.Async' }
@{ Pattern = '\.functional\.control\b'; Replacement = '.Functional.Control' } @{ Pattern = '\.functional\.control\b'; Replacement = '.Functional.Control' }
@{ Pattern = '\.functional\.functions\b'; Replacement = '.Functional.Functions' } @{ Pattern = '\.functional\.functions\b'; Replacement = '.Functional.Functions' }
@{ Pattern = '\.functional\.pipe\b'; Replacement = '.Functional.Pipe' } @{ Pattern = '\.functional\.pipe\b'; Replacement = '.Functional.Pipe' }
@{ Pattern = '\.functional\.result\b'; Replacement = '.Functional.Result' } @{ Pattern = '\.functional\.result\b'; Replacement = '.Functional.Result' }
@{ Pattern = '\.functional\b'; Replacement = '.Functional' } @{ Pattern = '\.functional\b'; Replacement = '.Functional' }
@{ Pattern = '\.services\.modules\b'; Replacement = '.Services.Modules' } @{ Pattern = '\.services\.modules\b'; Replacement = '.Services.Modules' }
@{ Pattern = '\.services\b'; Replacement = '.Services' } @{ Pattern = '\.services\b'; Replacement = '.Services' }
@{ Pattern = '\.architecture\b'; Replacement = '.Architecture' } @{ Pattern = '\.architecture\b'; Replacement = '.Architecture' }
@{ Pattern = '\.bases\b'; Replacement = '.Bases' } @{ Pattern = '\.bases\b'; Replacement = '.Bases' }
@{ Pattern = '\.command\b'; Replacement = '.Command' } @{ Pattern = '\.command\b'; Replacement = '.Command' }
@{ Pattern = '\.configuration\b'; Replacement = '.Configuration' } @{ Pattern = '\.configuration\b'; Replacement = '.Configuration' }
@{ Pattern = '\.constants\b'; Replacement = '.Constants' } @{ Pattern = '\.constants\b'; Replacement = '.Constants' }
@{ Pattern = '\.data\b'; Replacement = '.Data' } @{ Pattern = '\.data\b'; Replacement = '.Data' }
@{ Pattern = '\.enums\b'; Replacement = '.Enums' } @{ Pattern = '\.enums\b'; Replacement = '.Enums' }
@{ Pattern = '\.environment\b'; Replacement = '.Environment' } @{ Pattern = '\.environment\b'; Replacement = '.Environment' }
@{ Pattern = '\.extensions\b'; Replacement = '.Extensions' } @{ Pattern = '\.extensions\b'; Replacement = '.Extensions' }
@{ Pattern = '\.internals\b'; Replacement = '.Internals' } @{ Pattern = '\.internals\b'; Replacement = '.Internals' }
@{ Pattern = '\.ioc\b'; Replacement = '.IoC' } @{ Pattern = '\.ioc\b'; Replacement = '.IoC' }
@{ Pattern = '\.lifecycle\b'; Replacement = '.Lifecycle' } @{ Pattern = '\.lifecycle\b'; Replacement = '.Lifecycle' }
@{ Pattern = '\.model\b'; Replacement = '.Model' } @{ Pattern = '\.model\b'; Replacement = '.Model' }
@{ Pattern = '\.pause\b'; Replacement = '.Pause' } @{ Pattern = '\.pause\b'; Replacement = '.Pause' }
@{ Pattern = '\.pool\b'; Replacement = '.Pool' } @{ Pattern = '\.pool\b'; Replacement = '.Pool' }
@{ Pattern = '\.properties\b'; Replacement = '.Properties' } @{ Pattern = '\.properties\b'; Replacement = '.Properties' }
@{ Pattern = '\.property\b'; Replacement = '.Property' } @{ Pattern = '\.property\b'; Replacement = '.Property' }
@{ Pattern = '\.query\b'; Replacement = '.Query' } @{ Pattern = '\.query\b'; Replacement = '.Query' }
@{ Pattern = '\.registries\b'; Replacement = '.Registries' } @{ Pattern = '\.registries\b'; Replacement = '.Registries' }
@{ Pattern = '\.resource\b'; Replacement = '.Resource' } @{ Pattern = '\.resource\b'; Replacement = '.Resource' }
@{ Pattern = '\.rule\b'; Replacement = '.Rule' } @{ Pattern = '\.rule\b'; Replacement = '.Rule' }
@{ Pattern = '\.serializer\b'; Replacement = '.Serializer' } @{ Pattern = '\.serializer\b'; Replacement = '.Serializer' }
@{ Pattern = '\.state\b'; Replacement = '.State' } @{ Pattern = '\.state\b'; Replacement = '.State' }
@{ Pattern = '\.storage\b'; Replacement = '.Storage' } @{ Pattern = '\.storage\b'; Replacement = '.Storage' }
@{ Pattern = '\.system\b'; Replacement = '.System' } @{ Pattern = '\.system\b'; Replacement = '.System' }
@{ Pattern = '\.time\b'; Replacement = '.Time' } @{ Pattern = '\.time\b'; Replacement = '.Time' }
@{ Pattern = '\.utility\b'; Replacement = '.Utility' } @{ Pattern = '\.utility\b'; Replacement = '.Utility' }
@{ Pattern = '\.versioning\b'; Replacement = '.Versioning' } @{ Pattern = '\.versioning\b'; Replacement = '.Versioning' }
) )
foreach ($file in $mdFiles) { foreach ($file in $mdFiles) {
$content = Get-Content $file.FullName -Raw $content = Get-Content $file.FullName -Raw
$originalContent = $content $originalContent = $content
$fileReplacements = 0 $fileReplacements = 0
foreach ($rule in $namespaceRules) { foreach ($rule in $namespaceRules) {
$matches = [regex]::Matches($content, $rule.Pattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) $matches = [regex]::Matches($content, $rule.Pattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
if ($matches.Count -gt 0) { if ($matches.Count -gt 0) {
$content = [regex]::Replace($content, $rule.Pattern, $rule.Replacement, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) $content = [regex]::Replace($content, $rule.Pattern, $rule.Replacement, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
$fileReplacements += $matches.Count $fileReplacements += $matches.Count
} }
} }
if ($content -ne $originalContent) { if ($content -ne $originalContent) {
if ($DryRun) { if ($DryRun) {
Write-Info "[DRY RUN] 更新文档: $($file.FullName) ($fileReplacements 处替换)" Write-Info "[DRY RUN] 更新文档: $($file.FullName) ($fileReplacements 处替换)"
} else { } else {
Set-Content -Path $file.FullName -Value $content -NoNewline Set-Content -Path $file.FullName -Value $content -NoNewline
$updatedFiles++ $updatedFiles++
$totalReplacements += $fileReplacements $totalReplacements += $fileReplacements
Write-Info "更新: $($file.Name) ($fileReplacements 处替换)" Write-Info "更新: $($file.Name) ($fileReplacements 处替换)"
} }
} }
} }
if (-not $DryRun) { if (-not $DryRun) {
Write-Info "提交文档更新" Write-Info "提交文档更新"
git add -A git add -A
git commit -m "docs: 更新文档中的命名空间为 PascalCase" git commit -m "docs: 更新文档中的命名空间为 PascalCase"
} }
Write-Success "阶段 3 完成: 更新了 $updatedFiles 个文档,共 $totalReplacements 处替换" Write-Success "阶段 3 完成: 更新了 $updatedFiles 个文档,共 $totalReplacements 处替换"
} }
# 阶段 4: 验证 # 阶段 4: 验证
function Invoke-Verification { function Invoke-Verification {
Write-Info "=== 阶段 4: 验证 ===" Write-Info "=== 阶段 4: 验证 ==="
# 1. 编译验证 # 1. 编译验证
Write-Info "1. 编译验证..." Write-Info "1. 编译验证..."
if ($DryRun) { if ($DryRun) {
Write-Info "[DRY RUN] dotnet build" Write-Info "[DRY RUN] dotnet build"
} else { } else {
Push-Location $rootDir Push-Location $rootDir
try { try {
dotnet build --no-restore dotnet build --no-restore
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
Write-Success "编译成功" Write-Success "编译成功"
} else { } else {
Write-Error "编译失败" Write-Error "编译失败"
throw "编译失败" throw "编译失败"
} }
} finally { } finally {
Pop-Location Pop-Location
} }
} }
# 2. 测试验证 # 2. 测试验证
if (-not $SkipTests) { if (-not $SkipTests) {
Write-Info "2. 测试验证..." Write-Info "2. 测试验证..."
if ($DryRun) { if ($DryRun) {
Write-Info "[DRY RUN] dotnet test" Write-Info "[DRY RUN] dotnet test"
} else { } else {
Push-Location $rootDir Push-Location $rootDir
try { try {
dotnet test --no-build dotnet test --no-build
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
Write-Success "所有测试通过" Write-Success "所有测试通过"
} else { } else {
Write-Error "测试失败" Write-Error "测试失败"
throw "测试失败" throw "测试失败"
} }
} finally { } finally {
Pop-Location Pop-Location
} }
} }
} else { } else {
Write-Warning "跳过测试验证" Write-Warning "跳过测试验证"
} }
# 3. 检查残留的小写命名空间 # 3. 检查残留的小写命名空间
Write-Info "3. 检查残留的小写命名空间..." Write-Info "3. 检查残留的小写命名空间..."
$csFiles = Get-ChildItem -Path $rootDir -Filter "*.cs" -Recurse | $csFiles = Get-ChildItem -Path $rootDir -Filter "*.cs" -Recurse |
Where-Object { $_.FullName -notmatch "\\bin\\|\\obj\\|\\Generated\\" } Where-Object { $_.FullName -notmatch "\\bin\\|\\obj\\|\\Generated\\" }
$lowercasePatterns = @( $lowercasePatterns = @(
'\.architecture\b', '\.command\b', '\.configuration\b', '\.coroutine\b', '\.architecture\b', '\.command\b', '\.configuration\b', '\.coroutine\b',
'\.cqrs\b', '\.events\b', '\.extensions\b', '\.functional\b', '\.cqrs\b', '\.events\b', '\.extensions\b', '\.functional\b',
'\.ioc\b', '\.logging\b', '\.model\b', '\.query\b', '\.ioc\b', '\.logging\b', '\.model\b', '\.query\b',
'\.resource\b', '\.state\b', '\.system\b', '\.utility\b' '\.resource\b', '\.state\b', '\.system\b', '\.utility\b'
) )
$foundIssues = @() $foundIssues = @()
foreach ($file in $csFiles) { foreach ($file in $csFiles) {
$content = Get-Content $file.FullName -Raw $content = Get-Content $file.FullName -Raw
foreach ($pattern in $lowercasePatterns) { foreach ($pattern in $lowercasePatterns) {
if ($content -match $pattern) { if ($content -match $pattern) {
$foundIssues += "$($file.FullName): 找到小写命名空间 $pattern" $foundIssues += "$($file.FullName): 找到小写命名空间 $pattern"
} }
} }
} }
if ($foundIssues.Count -gt 0) { if ($foundIssues.Count -gt 0) {
Write-Warning "发现 $($foundIssues.Count) 个残留的小写命名空间:" Write-Warning "发现 $($foundIssues.Count) 个残留的小写命名空间:"
$foundIssues | ForEach-Object { Write-Warning $_ } $foundIssues | ForEach-Object { Write-Warning $_ }
} else { } else {
Write-Success "未发现残留的小写命名空间" Write-Success "未发现残留的小写命名空间"
} }
Write-Success "阶段 4 完成: 验证通过" Write-Success "阶段 4 完成: 验证通过"
} }
# 主执行逻辑 # 主执行逻辑
try { try {
Write-Info "GFramework 命名空间重构脚本" Write-Info "GFramework 命名空间重构脚本"
Write-Info "工作目录: $rootDir" Write-Info "工作目录: $rootDir"
Write-Info "配置文件: $mappingFile" Write-Info "配置文件: $mappingFile"
if ($DryRun) { if ($DryRun) {
Write-Warning "*** 干运行模式 - 不会执行实际操作 ***" Write-Warning "*** 干运行模式 - 不会执行实际操作 ***"
} }
if (-not (Test-Path $mappingFile)) { if (-not (Test-Path $mappingFile)) {
Write-Error "配置文件不存在: $mappingFile" Write-Error "配置文件不存在: $mappingFile"
exit 1 exit 1
} }
switch ($Phase) { switch ($Phase) {
"1" { Invoke-FolderRename } "1" { Invoke-FolderRename }
"2" { Invoke-NamespaceUpdate } "2" { Invoke-NamespaceUpdate }
"3" { Invoke-DocumentationUpdate } "3" { Invoke-DocumentationUpdate }
"4" { Invoke-Verification } "4" { Invoke-Verification }
"All" { "All" {
Invoke-FolderRename Invoke-FolderRename
Invoke-NamespaceUpdate Invoke-NamespaceUpdate
Invoke-DocumentationUpdate Invoke-DocumentationUpdate
Invoke-Verification Invoke-Verification
} }
} }
Write-Success "=== 重构完成 ===" Write-Success "=== 重构完成 ==="
} catch { } catch {
Write-Error "重构失败: $_" Write-Error "重构失败: $_"
Write-Error $_.ScriptStackTrace Write-Error $_.ScriptStackTrace
exit 1 exit 1
} }