713 字
4 分钟
C# 读写配置文件方案
一个防修改、保持完整性的读写配置文件方案。
ConfigManager.cs
public class ConfigManager{ private static readonly string ConfigPath = Path.Combine(AppContext.BaseDirectory, "config.dat"); // 配置文件路径,默认存储在程序根目录 private static readonly byte[] HmacKey = Encoding.UTF8.GetBytes("abcdefgh"); // 加密密钥
private Dictionary<string, object> _configData; private readonly object _lock = new();
public static ConfigManager Instance { get; } = new ConfigManager();
/// <summary> /// 构造函数 /// </summary> private ConfigManager() { Load(); }
/// <summary> /// 索引器 /// </summary> /// <param name="key"></param> /// <returns></returns> public object? this[string key] { get => Get(key); set => Set(key, value); }
/// <summary> /// 获取配置项 /// </summary> /// <param name="key"></param> /// <returns></returns> public object? Get(string key) { var sections = key.Split(':', StringSplitOptions.RemoveEmptyEntries); object? current = _configData;
foreach (var section in sections) { if (current is Dictionary<string, object> dict && dict.TryGetValue(section, out var next)) current = next; else return null; }
return current; }
/// <summary> /// 设置配置项 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void Set(string key, object? value) { var sections = key.Split(':', StringSplitOptions.RemoveEmptyEntries); lock (_lock) { var current = _configData; for (int i = 0; i < sections.Length - 1; i++) { var section = sections[i]; if (!current.TryGetValue(section, out var sub) || sub is not Dictionary<string, object>) { current[section] = new Dictionary<string, object>(); } current = (Dictionary<string, object>)current[section]; }
current[sections.Last()] = value!; } }
/// <summary> /// 存储配置文件 /// </summary> public void Save() { lock (_lock) { var json = JsonSerializer.Serialize(_configData); var compressed = Compress(Encoding.UTF8.GetBytes(json)); var signature = HMACSHA256.HashData(HmacKey, compressed); var finalData = signature.Concat(compressed).ToArray(); File.WriteAllBytes(ConfigPath, finalData); } }
/// <summary> /// 加载配置文件 /// </summary> private void Load() { try { if (!File.Exists(ConfigPath)) { // 如果配置文件不存在,可以选择创建一个默认配置 _configData = []; Save(); //throw new FileNotFoundException(); }
var bytes = File.ReadAllBytes(ConfigPath); if (bytes.Length < 32) throw new Exception("配置文件不完整");
var signature = bytes.Take(32).ToArray(); var content = bytes.Skip(32).ToArray(); var actualSignature = HMACSHA256.HashData(HmacKey, content);
if (!signature.SequenceEqual(actualSignature)) throw new Exception("配置文件已被篡改");
var decompressed = Decompress(content); _configData = JsonSerializer.Deserialize<Dictionary<string, object>>(decompressed)!; } catch { // 可以在这里加载默认配置
_configData = []; System.Windows.MessageBox.Show("配置文件损坏或不存在,已加载默认配置", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); } }
/// <summary> /// 压缩配置文件 /// </summary> /// <param name="data"></param> /// <returns></returns> private static byte[] Compress(byte[] data) { using var output = new MemoryStream(); using (var gz = new GZipStream(output, CompressionLevel.Optimal)) gz.Write(data, 0, data.Length); return output.ToArray(); }
/// <summary> /// 解压缩配置文件 /// </summary> /// <param name="data"></param> /// <returns></returns> private static string Decompress(byte[] data) { using var input = new MemoryStream(data); using var gz = new GZipStream(input, CompressionMode.Decompress); using var reader = new StreamReader(gz, Encoding.UTF8); return reader.ReadToEnd(); }}SysConfig.cs
/// <summary>/// 系统配置管理/// </summary>public static class SysConfig{ /// <summary> /// 获取配置项 /// </summary> /// <param name="key"></param> /// <returns></returns> public static string? Get(string key)=> Get<string>(key);
/// <summary> /// 获取配置项 /// </summary> public static T? Get<T>(string key) { var value = ConfigManager.Instance.Get(key); if (value == null) return default;
try { if (value is JsonElement jsonElement) { value = jsonElement.ToString(); } return (T)Convert.ChangeType(value, typeof(T)); } catch { return default; } }
/// <summary> /// 设置配置项 /// </summary> public static void Set(string key, object? value) { ConfigManager.Instance.Set(key, value); }
/// <summary> /// 设置配置项并保存 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public static void SetAndSave(string key, object? value) { ConfigManager.Instance.Set(key, value); ConfigManager.Instance.Save(); }
/// <summary> /// 保存配置 /// </summary> public static void Save() { ConfigManager.Instance.Save(); }}使用示例:
// 获取SysConfig.Get("FirstConfig");SysConfig.Get<int>("ConvertIntConfig");SysConfig.Get<bool>("Convert:FirstBoolVal");
// 设置SysConfig.Set("FirstConfig", "保存值");SysConfig.Set("Convert:FirstBoolVal", true);
// 保存配置项SysConfig.Save();
// 上述[设置]和[保存配置项]可使用下面一步完成SysConfig.SetAndSave("ConvertIntConfig", 100);