diff --git a/GFramework.Core.Abstractions/configuration/IConfigurationManager.cs b/GFramework.Core.Abstractions/configuration/IConfigurationManager.cs
new file mode 100644
index 0000000..d75fff5
--- /dev/null
+++ b/GFramework.Core.Abstractions/configuration/IConfigurationManager.cs
@@ -0,0 +1,99 @@
+using GFramework.Core.Abstractions.events;
+using GFramework.Core.Abstractions.utility;
+
+namespace GFramework.Core.Abstractions.configuration;
+
+///
+/// 配置管理器接口,提供类型安全的配置存储和访问
+/// 线程安全:所有方法都是线程安全的
+///
+public interface IConfigurationManager : IUtility
+{
+ ///
+ /// 获取配置数量
+ ///
+ int Count { get; }
+
+ ///
+ /// 获取指定键的配置值
+ ///
+ /// 配置值类型
+ /// 配置键
+ /// 配置值,如果不存在则返回类型默认值
+ T? GetConfig(string key);
+
+ ///
+ /// 获取指定键的配置值,如果不存在则返回默认值
+ ///
+ /// 配置值类型
+ /// 配置键
+ /// 默认值
+ /// 配置值或默认值
+ T GetConfig(string key, T defaultValue);
+
+ ///
+ /// 设置指定键的配置值
+ ///
+ /// 配置值类型
+ /// 配置键
+ /// 配置值
+ void SetConfig(string key, T value);
+
+ ///
+ /// 检查指定键的配置是否存在
+ ///
+ /// 配置键
+ /// 如果存在返回 true,否则返回 false
+ bool HasConfig(string key);
+
+ ///
+ /// 移除指定键的配置
+ ///
+ /// 配置键
+ /// 如果成功移除返回 true,否则返回 false
+ bool RemoveConfig(string key);
+
+ ///
+ /// 清空所有配置
+ ///
+ void Clear();
+
+ ///
+ /// 监听指定键的配置变化
+ ///
+ /// 配置值类型
+ /// 配置键
+ /// 配置变化时的回调,参数为新值
+ /// 取消注册接口
+ IUnRegister WatchConfig(string key, Action onChange);
+
+ ///
+ /// 从 JSON 字符串加载配置
+ ///
+ /// JSON 字符串
+ void LoadFromJson(string json);
+
+ ///
+ /// 将配置保存为 JSON 字符串
+ ///
+ /// JSON 字符串
+ string SaveToJson();
+
+ ///
+ /// 从文件加载配置
+ ///
+ /// 文件路径
+ void LoadFromFile(string path);
+
+ ///
+ /// 将配置保存到文件
+ ///
+ /// 文件路径
+ void SaveToFile(string path);
+
+ ///
+ /// 获取所有配置键
+ ///
+ /// 配置键集合
+ IEnumerable GetAllKeys();
+}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/configuration/ConfigurationManagerTests.cs b/GFramework.Core.Tests/configuration/ConfigurationManagerTests.cs
new file mode 100644
index 0000000..45a38dd
--- /dev/null
+++ b/GFramework.Core.Tests/configuration/ConfigurationManagerTests.cs
@@ -0,0 +1,377 @@
+using System.IO;
+using GFramework.Core.configuration;
+using NUnit.Framework;
+
+namespace GFramework.Core.Tests.configuration;
+
+///
+/// ConfigurationManager 功能测试类
+///
+[TestFixture]
+public class ConfigurationManagerTests
+{
+ [SetUp]
+ public void SetUp()
+ {
+ _configManager = new ConfigurationManager();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ _configManager.Clear();
+ }
+
+ private ConfigurationManager _configManager = null!;
+
+ [Test]
+ public void SetConfig_And_GetConfig_Should_Work()
+ {
+ _configManager.SetConfig("test.key", 42);
+
+ var value = _configManager.GetConfig("test.key");
+
+ Assert.That(value, Is.EqualTo(42));
+ }
+
+ [Test]
+ public void GetConfig_With_DefaultValue_Should_Return_DefaultValue_When_Key_Not_Exists()
+ {
+ var value = _configManager.GetConfig("nonexistent.key", 100);
+
+ Assert.That(value, Is.EqualTo(100));
+ }
+
+ [Test]
+ public void GetConfig_Should_Return_Default_When_Key_Not_Exists()
+ {
+ var value = _configManager.GetConfig("nonexistent.key");
+
+ Assert.That(value, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void HasConfig_Should_Return_True_When_Key_Exists()
+ {
+ _configManager.SetConfig("test.key", "value");
+
+ Assert.That(_configManager.HasConfig("test.key"), Is.True);
+ }
+
+ [Test]
+ public void HasConfig_Should_Return_False_When_Key_Not_Exists()
+ {
+ Assert.That(_configManager.HasConfig("nonexistent.key"), Is.False);
+ }
+
+ [Test]
+ public void RemoveConfig_Should_Remove_Existing_Key()
+ {
+ _configManager.SetConfig("test.key", "value");
+
+ var removed = _configManager.RemoveConfig("test.key");
+
+ Assert.That(removed, Is.True);
+ Assert.That(_configManager.HasConfig("test.key"), Is.False);
+ }
+
+ [Test]
+ public void RemoveConfig_Should_Return_False_When_Key_Not_Exists()
+ {
+ var removed = _configManager.RemoveConfig("nonexistent.key");
+
+ Assert.That(removed, Is.False);
+ }
+
+ [Test]
+ public void Clear_Should_Remove_All_Configs()
+ {
+ _configManager.SetConfig("key1", "value1");
+ _configManager.SetConfig("key2", "value2");
+
+ _configManager.Clear();
+
+ Assert.That(_configManager.Count, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Count_Should_Return_Correct_Number()
+ {
+ _configManager.SetConfig("key1", "value1");
+ _configManager.SetConfig("key2", "value2");
+ _configManager.SetConfig("key3", "value3");
+
+ Assert.That(_configManager.Count, Is.EqualTo(3));
+ }
+
+ [Test]
+ public void GetAllKeys_Should_Return_All_Keys()
+ {
+ _configManager.SetConfig("key1", "value1");
+ _configManager.SetConfig("key2", "value2");
+
+ var keys = _configManager.GetAllKeys().ToList();
+
+ Assert.That(keys, Has.Count.EqualTo(2));
+ Assert.That(keys, Contains.Item("key1"));
+ Assert.That(keys, Contains.Item("key2"));
+ }
+
+ [Test]
+ public void GetConfig_Should_Support_Different_Types()
+ {
+ _configManager.SetConfig("int.key", 42);
+ _configManager.SetConfig("string.key", "hello");
+ _configManager.SetConfig("bool.key", true);
+ _configManager.SetConfig("double.key", 3.14);
+
+ Assert.That(_configManager.GetConfig("int.key"), Is.EqualTo(42));
+ Assert.That(_configManager.GetConfig("string.key"), Is.EqualTo("hello"));
+ Assert.That(_configManager.GetConfig("bool.key"), Is.True);
+ Assert.That(_configManager.GetConfig("double.key"), Is.EqualTo(3.14).Within(0.001));
+ }
+
+ [Test]
+ public void WatchConfig_Should_Trigger_When_Config_Changes()
+ {
+ var callCount = 0;
+ var receivedValue = 0;
+
+ _configManager.WatchConfig("test.key", value =>
+ {
+ callCount++;
+ receivedValue = value;
+ });
+
+ _configManager.SetConfig("test.key", 42);
+
+ Assert.That(callCount, Is.EqualTo(1));
+ Assert.That(receivedValue, Is.EqualTo(42));
+ }
+
+ [Test]
+ public void WatchConfig_Should_Not_Trigger_When_Value_Not_Changed()
+ {
+ _configManager.SetConfig("test.key", 42);
+
+ var callCount = 0;
+ _configManager.WatchConfig("test.key", _ => callCount++);
+
+ _configManager.SetConfig("test.key", 42);
+
+ Assert.That(callCount, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void UnRegister_Should_Stop_Watching()
+ {
+ var callCount = 0;
+
+ var unRegister = _configManager.WatchConfig("test.key", _ => callCount++);
+
+ _configManager.SetConfig("test.key", 42);
+ Assert.That(callCount, Is.EqualTo(1));
+
+ unRegister.UnRegister();
+
+ _configManager.SetConfig("test.key", 100);
+ Assert.That(callCount, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void Multiple_Watchers_Should_All_Be_Triggered()
+ {
+ var count1 = 0;
+ var count2 = 0;
+
+ _configManager.WatchConfig("test.key", _ => count1++);
+ _configManager.WatchConfig("test.key", _ => count2++);
+
+ _configManager.SetConfig("test.key", 42);
+
+ Assert.That(count1, Is.EqualTo(1));
+ Assert.That(count2, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void SaveToJson_And_LoadFromJson_Should_Work()
+ {
+ _configManager.SetConfig("key1", "value1");
+ _configManager.SetConfig("key2", 42);
+
+ var json = _configManager.SaveToJson();
+
+ var newManager = new ConfigurationManager();
+ newManager.LoadFromJson(json);
+
+ Assert.That(newManager.GetConfig("key1"), Is.EqualTo("value1"));
+ Assert.That(newManager.GetConfig("key2"), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void SaveToFile_And_LoadFromFile_Should_Work()
+ {
+ var tempFile = Path.GetTempFileName();
+
+ try
+ {
+ _configManager.SetConfig("key1", "value1");
+ _configManager.SetConfig("key2", 42);
+
+ _configManager.SaveToFile(tempFile);
+
+ var newManager = new ConfigurationManager();
+ newManager.LoadFromFile(tempFile);
+
+ Assert.That(newManager.GetConfig("key1"), Is.EqualTo("value1"));
+ Assert.That(newManager.GetConfig("key2"), Is.EqualTo(42));
+ }
+ finally
+ {
+ if (File.Exists(tempFile))
+ {
+ File.Delete(tempFile);
+ }
+ }
+ }
+
+ [Test]
+ public void LoadFromFile_Should_Throw_When_File_Not_Exists()
+ {
+ Assert.Throws(() => { _configManager.LoadFromFile("nonexistent.json"); });
+ }
+
+ [Test]
+ public void SetConfig_Should_Throw_When_Key_Is_Null_Or_Empty()
+ {
+ Assert.Throws(() => _configManager.SetConfig(null!, "value"));
+ Assert.Throws(() => _configManager.SetConfig("", "value"));
+ Assert.Throws(() => _configManager.SetConfig(" ", "value"));
+ }
+
+ [Test]
+ public void GetConfig_Should_Throw_When_Key_Is_Null_Or_Empty()
+ {
+ Assert.Throws(() => _configManager.GetConfig(null!));
+ Assert.Throws(() => _configManager.GetConfig(""));
+ Assert.Throws(() => _configManager.GetConfig(" "));
+ }
+
+ [Test]
+ public void WatchConfig_Should_Throw_When_Parameters_Invalid()
+ {
+ Assert.Throws(() => _configManager.WatchConfig(null!, _ => { }));
+ Assert.Throws(() => _configManager.WatchConfig("key", null!));
+ }
+
+ [Test]
+ public void Concurrent_SetConfig_Should_Be_Thread_Safe()
+ {
+ const int threadCount = 10;
+ const int iterationsPerThread = 100;
+ var tasks = new Task[threadCount];
+ var exceptions = new List();
+
+ for (var i = 0; i < threadCount; i++)
+ {
+ var threadId = i;
+ tasks[i] = Task.Run(() =>
+ {
+ try
+ {
+ for (var j = 0; j < iterationsPerThread; j++)
+ {
+ _configManager.SetConfig($"key.{threadId}", j);
+ }
+ }
+ catch (Exception ex)
+ {
+ lock (exceptions)
+ {
+ exceptions.Add(ex);
+ }
+ }
+ });
+ }
+
+ Task.WaitAll(tasks);
+
+ Assert.That(exceptions, Is.Empty);
+ }
+
+ [Test]
+ public void Concurrent_GetConfig_And_SetConfig_Should_Be_Thread_Safe()
+ {
+ const int threadCount = 5;
+ var tasks = new Task[threadCount];
+ var exceptions = new List();
+
+ for (var i = 0; i < threadCount; i++)
+ {
+ tasks[i] = Task.Run(() =>
+ {
+ try
+ {
+ for (var j = 0; j < 100; j++)
+ {
+ if (j % 2 == 0)
+ {
+ _configManager.SetConfig("shared.key", j);
+ }
+ else
+ {
+ _configManager.GetConfig("shared.key");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ lock (exceptions)
+ {
+ exceptions.Add(ex);
+ }
+ }
+ });
+ }
+
+ Task.WaitAll(tasks);
+
+ Assert.That(exceptions, Is.Empty);
+ }
+
+ [Test]
+ public void Concurrent_WatchConfig_Should_Be_Thread_Safe()
+ {
+ const int threadCount = 10;
+ var tasks = new Task[threadCount];
+ var exceptions = new List();
+ var callCounts = new int[threadCount];
+
+ for (var i = 0; i < threadCount; i++)
+ {
+ var threadId = i;
+ tasks[i] = Task.Run(() =>
+ {
+ try
+ {
+ _configManager.WatchConfig("test.key",
+ _ => { Interlocked.Increment(ref callCounts[threadId]); });
+ }
+ catch (Exception ex)
+ {
+ lock (exceptions)
+ {
+ exceptions.Add(ex);
+ }
+ }
+ });
+ }
+
+ Task.WaitAll(tasks);
+
+ _configManager.SetConfig("test.key", 42);
+
+ Assert.That(exceptions, Is.Empty);
+ Assert.That(callCounts.Sum(), Is.EqualTo(threadCount));
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/configuration/ConfigWatcherUnRegister.cs b/GFramework.Core/configuration/ConfigWatcherUnRegister.cs
new file mode 100644
index 0000000..78b6199
--- /dev/null
+++ b/GFramework.Core/configuration/ConfigWatcherUnRegister.cs
@@ -0,0 +1,21 @@
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.Abstractions.configuration;
+
+///
+/// 配置监听取消注册接口
+///
+internal sealed class ConfigWatcherUnRegister : IUnRegister
+{
+ private readonly Action _unRegisterAction;
+
+ public ConfigWatcherUnRegister(Action unRegisterAction)
+ {
+ _unRegisterAction = unRegisterAction ?? throw new ArgumentNullException(nameof(unRegisterAction));
+ }
+
+ public void UnRegister()
+ {
+ _unRegisterAction();
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/configuration/ConfigurationManager.cs b/GFramework.Core/configuration/ConfigurationManager.cs
new file mode 100644
index 0000000..3bf749c
--- /dev/null
+++ b/GFramework.Core/configuration/ConfigurationManager.cs
@@ -0,0 +1,324 @@
+using System.Collections.Concurrent;
+using System.IO;
+using System.Text.Json;
+using GFramework.Core.Abstractions.configuration;
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.configuration;
+
+///
+/// 配置管理器实现,提供线程安全的配置存储和访问
+/// 线程安全:所有公共方法都是线程安全的
+///
+public class ConfigurationManager : IConfigurationManager
+{
+ ///
+ /// Key 参数验证错误消息常量
+ ///
+ private const string KeyCannotBeNullOrEmptyMessage = "Key cannot be null or whitespace.";
+
+ ///
+ /// Path 参数验证错误消息常量
+ ///
+ private const string PathCannotBeNullOrEmptyMessage = "Path cannot be null or whitespace.";
+
+ ///
+ /// JSON 参数验证错误消息常量
+ ///
+ private const string JsonCannotBeNullOrEmptyMessage = "JSON cannot be null or whitespace.";
+
+ ///
+ /// 配置存储字典(线程安全)
+ ///
+ private readonly ConcurrentDictionary _configs = new();
+
+ ///
+ /// 用于保护监听器列表的锁
+ ///
+ private readonly object _watcherLock = new();
+
+ ///
+ /// 配置监听器字典(线程安全)
+ /// 键:配置键,值:监听器列表
+ ///
+ private readonly ConcurrentDictionary> _watchers = new();
+
+ ///
+ /// 获取配置数量
+ ///
+ public int Count => _configs.Count;
+
+ ///
+ /// 获取指定键的配置值
+ ///
+ public T? GetConfig(string key)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ throw new ArgumentException(KeyCannotBeNullOrEmptyMessage, nameof(key));
+
+ if (_configs.TryGetValue(key, out var value))
+ {
+ return ConvertValue(value);
+ }
+
+ return default;
+ }
+
+ ///
+ /// 获取指定键的配置值,如果不存在则返回默认值
+ ///
+ public T GetConfig(string key, T defaultValue)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ throw new ArgumentException(KeyCannotBeNullOrEmptyMessage, nameof(key));
+
+ if (_configs.TryGetValue(key, out var value))
+ {
+ return ConvertValue(value);
+ }
+
+ return defaultValue;
+ }
+
+ ///
+ /// 设置指定键的配置值
+ ///
+ public void SetConfig(string key, T value)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ throw new ArgumentException(KeyCannotBeNullOrEmptyMessage, nameof(key));
+
+ var oldValue = _configs.AddOrUpdate(key, value!, (_, _) => value!);
+
+ // 触发监听器
+ if (!EqualityComparer