diff --git a/GFramework.Core.Abstractions/versioning/IVersioned.cs b/GFramework.Core.Abstractions/versioning/IVersioned.cs
new file mode 100644
index 0000000..ea39d70
--- /dev/null
+++ b/GFramework.Core.Abstractions/versioning/IVersioned.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace GFramework.Core.Abstractions.versioning;
+
+///
+/// 定义具有版本信息的接口
+///
+public interface IVersioned
+{
+ ///
+ /// 获取对象的版本号
+ ///
+ int Version { get; }
+}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/setting/ISettingsMigration.cs b/GFramework.Game.Abstractions/setting/ISettingsMigration.cs
new file mode 100644
index 0000000..ee8388c
--- /dev/null
+++ b/GFramework.Game.Abstractions/setting/ISettingsMigration.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace GFramework.Game.Abstractions.setting;
+
+///
+/// 定义设置数据迁移接口,用于处理不同版本设置数据之间的转换
+///
+public interface ISettingsMigration
+{
+ ///
+ /// 获取要迁移的设置类型
+ ///
+ Type SettingsType { get; }
+
+ ///
+ /// 获取源版本号(迁移前的版本)
+ ///
+ int FromVersion { get; }
+
+ ///
+ /// 获取目标版本号(迁移后的版本)
+ ///
+ int ToVersion { get; }
+
+ ///
+ /// 执行设置数据迁移操作
+ ///
+ /// 需要迁移的旧版设置数据
+ /// 迁移后的新版设置数据
+ ISettingsSection Migrate(ISettingsSection oldData);
+}
\ No newline at end of file
diff --git a/GFramework.Game/setting/SettingsModel.cs b/GFramework.Game/setting/SettingsModel.cs
index 6608f89..4f9f7f3 100644
--- a/GFramework.Game/setting/SettingsModel.cs
+++ b/GFramework.Game/setting/SettingsModel.cs
@@ -1,4 +1,5 @@
-using GFramework.Core.extensions;
+using GFramework.Core.Abstractions.versioning;
+using GFramework.Core.extensions;
using GFramework.Core.model;
using GFramework.Game.Abstractions.setting;
@@ -11,87 +12,91 @@ public class SettingsModel : AbstractModel, ISettingsModel
{
private readonly Dictionary _applicators = new();
private readonly Dictionary _dataSettings = new();
+ private readonly Dictionary<(Type type, int from), ISettingsMigration> _migrations = new();
+
private ISettingsPersistence? _persistence;
+ // -----------------------------
+ // Data
+ // -----------------------------
+
///
- /// 获取或创建数据设置
+ /// 获取指定类型的设置数据实例,如果不存在则创建新的实例
///
- /// 设置数据类型,必须实现ISettingsData接口并具有无参构造函数
+ /// 设置数据类型,必须实现ISettingsData接口并提供无参构造函数
/// 指定类型的设置数据实例
public T GetData() where T : class, ISettingsData, new()
{
var type = typeof(T);
+ if (_dataSettings.TryGetValue(type, out var data))
+ return (T)data;
- // 尝试从现有字典中获取已存在的设置数据
- if (_dataSettings.TryGetValue(type, out var existing))
- return (T)existing;
-
- // 创建新的设置数据实例并存储到字典中
var created = new T();
_dataSettings[type] = created;
return created;
}
///
- /// 获取所有已注册的可应用设置
+ /// 获取所有设置数据的枚举集合
///
- /// 所有可应用设置的枚举集合
- public IEnumerable AllApplicators()
- {
- return _applicators.Values;
- }
+ /// 所有设置数据的枚举集合
+ public IEnumerable AllData()
+ => _dataSettings.Values;
+
+ // -----------------------------
+ // Applicator
+ // -----------------------------
///
- /// 注册可应用设置(必须手动注册)
+ /// 获取所有设置应用器的枚举集合
///
- /// 可应用设置的类型,必须继承自class和IApplyAbleSettings
- /// 要注册的可应用设置实例
- /// 返回当前设置模型实例,支持链式调用
- public ISettingsModel RegisterApplicator(T applicator) where T : class, IApplyAbleSettings
+ /// 所有设置应用器的枚举集合
+ public IEnumerable AllApplicators()
+ => _applicators.Values;
+
+ ///
+ /// 注册设置应用器到模型中
+ ///
+ /// 设置应用器类型,必须实现IApplyAbleSettings接口
+ /// 要注册的设置应用器实例
+ /// 当前设置模型实例,支持链式调用
+ public ISettingsModel RegisterApplicator(T applicator)
+ where T : class, IApplyAbleSettings
{
- var type = typeof(T);
- _applicators[type] = applicator;
+ _applicators[typeof(T)] = applicator;
return this;
}
///
- /// 获取已注册的可应用设置
+ /// 获取指定类型的设置应用器实例
///
- /// 可应用设置类型,必须实现IApplyAbleSettings接口
- /// 找到的可应用设置实例,如果未找到则返回null
+ /// 设置应用器类型,必须实现IApplyAbleSettings接口
+ /// 指定类型的设置应用器实例,如果不存在则返回null
public T? GetApplicator() where T : class, IApplyAbleSettings
{
- var type = typeof(T);
- return _applicators.TryGetValue(type, out var applicator)
- ? (T)applicator
+ return _applicators.TryGetValue(typeof(T), out var app)
+ ? (T)app
: null;
}
- ///
- /// 获取所有设置数据
- ///
- /// 所有设置数据的枚举集合
- public IEnumerable AllData()
- {
- return _dataSettings.Values;
- }
+ // -----------------------------
+ // Section lookup
+ // -----------------------------
///
- /// 尝试获取指定类型的设置节
+ /// 尝试获取指定类型的设置节
///
/// 要查找的设置类型
/// 输出参数,找到的设置节实例
- /// 如果找到设置节则返回true,否则返回false
+ /// 如果找到对应类型的设置节则返回true,否则返回false
public bool TryGet(Type type, out ISettingsSection section)
{
- // 首先在数据设置字典中查找
if (_dataSettings.TryGetValue(type, out var data))
{
section = data;
return true;
}
- // 然后在应用器字典中查找
if (_applicators.TryGetValue(type, out var applicator))
{
section = applicator;
@@ -102,9 +107,49 @@ public class SettingsModel : AbstractModel, ISettingsModel
return false;
}
+ // -----------------------------
+ // Migration
+ // -----------------------------
///
- /// 初始化并加载指定类型的设置数据
+ /// 注册设置迁移器到模型中
+ ///
+ /// 要注册的设置迁移器实例
+ /// 当前设置模型实例,支持链式调用
+ public ISettingsModel RegisterMigration(ISettingsMigration migration)
+ {
+ _migrations[(migration.SettingsType, migration.FromVersion)] = migration;
+ return this;
+ }
+
+ ///
+ /// 如果需要的话,对设置节进行版本迁移
+ ///
+ /// 待检查和迁移的设置节
+ /// 迁移后的设置节
+ private ISettingsSection MigrateIfNeeded(ISettingsSection section)
+ {
+ if (section is not IVersioned versioned)
+ return section;
+
+ var type = section.GetType();
+ var current = section;
+
+ while (_migrations.TryGetValue((type, versioned.Version), out var migration))
+ {
+ current = migration.Migrate(current);
+ versioned = (IVersioned)current;
+ }
+
+ return current;
+ }
+
+ // -----------------------------
+ // Load / Init
+ // -----------------------------
+
+ ///
+ /// 异步初始化设置模型,加载指定类型的设置数据
///
/// 要初始化的设置类型数组
public async Task InitializeAsync(params Type[] settingTypes)
@@ -116,7 +161,7 @@ public class SettingsModel : AbstractModel, ISettingsModel
type.GetConstructor(Type.EmptyTypes) == null)
continue;
- // 使用反射调用泛型方法 LoadAsync
+ // Load()
var method = typeof(ISettingsPersistence)
.GetMethod(nameof(ISettingsPersistence.LoadAsync))!
.MakeGenericMethod(type);
@@ -124,14 +169,17 @@ public class SettingsModel : AbstractModel, ISettingsModel
var task = (Task)method.Invoke(_persistence, null)!;
await task;
- var loaded = (ISettingsData)((dynamic)task).Result;
- _dataSettings[type] = loaded;
+ var loaded = (ISettingsSection)((dynamic)task).Result;
+
+ // ★ 关键:迁移
+ var migrated = MigrateIfNeeded(loaded);
+
+ _dataSettings[type] = (ISettingsData)migrated;
}
}
-
///
- /// 初始化方法,用于执行模型的初始化逻辑
+ /// 初始化方法,用于获取设置持久化服务
///
protected override void OnInit()
{