GeWuYou d1cdac8082 docs: 更新文档链接和代码块格式
- 修复了文档中多个文件的链接指向,统一使用相对路径格式
- 移除了代码块中的语言标识符以保持一致性
- 更新了存储模块文档中的类名显示方式
- 修正了架构文档中的跨文档引用链接
- 调整了事件、命令、查询等核心模块的文档链接结构
- 规范化了所有代码示例的格式和引用方式
2026-02-11 14:44:46 +08:00

134 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# GFramework 存储模块使用指南
本模块提供了基于文件系统的存储功能包括两个主要类FileStorage
和 ScopedStorage。
## FileStorage
FileStorage 是一个基于文件系统的存储实现,它将数据以序列化形式保存到磁盘上的指定目录。
### 特性
- 将数据保存为文件(默认扩展名为 `.dat`
- 支持同步和异步操作
- 自动创建存储目录
- 防止路径遍历攻击(过滤非法文件名字符)
- 通过细粒度锁机制保证线程安全(每个键都有独立的锁)
### 构造函数
```csharp
FileStorage(string rootPath, ISerializer serializer, string extension = ".dat")
```
### 使用示例
```csharp
// 创建 JSON 序列化器
var serializer = new JsonSerializer();
// 创建文件存储实例
var storage = new FileStorage(@"C:\MyGame\Data", serializer);
// 写入数据
storage.Write("player_score", 1000);
storage.Write("player_settings", new { Volume = 0.8f, Difficulty = "Hard" });
// 读取数据
int score = storage.Read<int>("player_score");
var settings = storage.Read("player_settings", new { Volume = 0.5f, Difficulty = "Normal" });
// 异步读取数据
Task<int> scoreTask = storage.ReadAsync<int>("player_score");
// 检查数据是否存在
if (storage.Exists("player_score"))
{
// 数据存在
}
// 异步检查数据是否存在
Task<bool> existsTask = storage.ExistsAsync("player_score");
// 删除数据
storage.Delete("player_score");
// 异步写入数据
storage.WriteAsync("player_score", 1200);
```
## ScopedStorage
ScopedStorage 是一个装饰器模式的实现,它为所有存储键添加前缀,从而实现逻辑分组和命名空间隔离。
### 特性
- 为所有键添加指定前缀
- 透明地包装底层存储实现
- 支持嵌套作用域
- 与底层存储共享物理存储
- 支持同步和异步操作
### 构造函数
```csharp
ScopedStorage(IStorage inner, string prefix)
```
### 使用示例
```csharp
// 基于文件存储创建带作用域的存储
var scopedStorage = new ScopedStorage(fileStorage, "game_settings");
// 所有操作都会添加前缀 "game_settings/"
scopedStorage.Write("volume", 0.8f); // 实际存储为 "game_settings/volume.dat"
scopedStorage.Write("theme", "dark"); // 实际存储为 "game_settings/theme.dat"
// 读取操作同样适用前缀
float volume = scopedStorage.Read<float>("volume"); // 从 "game_settings/volume.dat" 读取
// 创建嵌套作用域
var nestedStorage = scopedStorage.Scope("graphics"); // 前缀变为 "game_settings/graphics/"
nestedStorage.Write("resolution", "1920x1080"); // 实际存储为 "game_settings/graphics/resolution.dat"
// 异步操作
await scopedStorage.WriteAsync("audio", new { MasterVolume = 0.9f });
var audioSettings = await scopedStorage.ReadAsync<object>("audio");
```
## 组合使用示例
```csharp
// 创建基础序列化器和文件存储
var serializer = new JsonSerializer();
var baseStorage = new FileStorage(@"C:\MyGame\Data", serializer);
// 创建不同作用域的存储
var playerStorage = new ScopedStorage(baseStorage, "player");
var gameStorage = new ScopedStorage(baseStorage, "game");
var settingsStorage = new ScopedStorage(baseStorage, "settings");
// 在不同的作用域中使用相同键而不会冲突
playerStorage.Write("level", 5);
gameStorage.Write("level", "forest_area_1");
settingsStorage.Write("level", "high");
// 结果是三个不同的文件:
// - player/level.dat
// - game/level.dat
// - settings/level.dat
```
## 注意事项
1. **序列化器选择**:确保使用的 ISerializer
实现能够正确处理你要存储的数据类型。
2. **错误处理**FileStorage 的 `Read<T>(string key)` 方法会在键不存在时抛出异常,可以使用
`Read<T>(string key, T defaultValue)` 来避免异常。
3. **线程安全**FileStorage 通过细粒度锁机制保证线程安全,每个键都有独立的锁,因此不同键的操作可以并发执行。
4. **文件权限**:确保应用程序对指定的存储目录有读写权限。
5. **路径安全**FileStorage 会自动防止路径遍历攻击,因此键不能包含 `..`,并且特殊字符会被替换为下划线。
6. **存储键格式**:键可以包含 `/` 作为分隔符,这将被转换为相应的目录层级,例如 `"player/profile"` 会存储在
`player/profile.dat` 文件中。
7. **异步操作**尽管异步读写操作使用了异步IO但仍会使用锁来保证对同一键的并发访问安全。