如何将XML配置反序列化到应用配置的Singleton单例实例中?
最优方案推荐
方案1:单例构造函数内置持久化加载逻辑(最推荐)
直接在单例的私有构造函数中完成XML配置的读取和属性填充,不需要修改单例的对外结构,也不需要额外增加嵌套类,配置访问路径完全保持原样:
public sealed class AppConfig { private static readonly Lazy<AppConfig> appConfig = new(() => new AppConfig()); public static AppConfig Settings => appConfig.Value; // 配置文件路径可自行统一维护 private const string ConfigFilePath = "appconfig.xml"; private AppConfig() { // 构造时自动加载已持久化的配置 if (File.Exists(ConfigFilePath)) { using var sr = new StreamReader(ConfigFilePath); var xs = new XmlSerializer(typeof(AppConfig)); // 反序列化得到临时对象 var loadedConfig = (AppConfig)xs.Deserialize(sr); // 把临时对象的属性赋值到当前单例实例 this.SomeValue = loadedConfig.SomeValue; // 后续新增配置项只需要在这里加一行赋值即可 } } [XmlElement] public string SomeValue { get; set; } // 持久化方法也内置到单例类里,调用更方便 public void Save() { using var sw = new StreamWriter(ConfigFilePath); var xs = new XmlSerializer(typeof(AppConfig)); xs.Serialize(sw, this); } }
用这个方案的话,外部使用逻辑极其简洁:
// 读配置 var val = AppConfig.Settings.SomeValue; // 改配置 AppConfig.Settings.SomeValue = "新值"; // 持久化到磁盘 AppConfig.Settings.Save();
方案2:通用属性拷贝扩展方法(适合不想修改单例内部逻辑的场景)
如果不想把加载逻辑放到构造函数里,可以写一个通用的对象属性拷贝扩展方法,反序列化得到新对象后直接把属性批量拷贝到单例实例,不需要新增嵌套类:
// 通用扩展方法,放到公共工具类里即可 public static class ObjectExtensions { public static void CopyPropertiesFrom<T>(this T target, T source) { var properties = typeof(T).GetProperties() .Where(p => p.CanRead && p.CanWrite); foreach (var prop in properties) { prop.SetValue(target, prop.GetValue(source)); } } }
反序列化时代码:
var xs = new XmlSerializer(typeof(AppConfig)); using var sr = new StreamReader(fileName); var loadedConfig = (AppConfig)xs.Deserialize(sr); // 直接把属性拷贝到已有的单例实例,不需要替换实例 AppConfig.Settings.CopyPropertiesFrom(loadedConfig);
关于去掉readonly修饰符的问题
绝对不建议这么做。readonly修饰的Lazy<AppConfig>字段是保证单例全局唯一性、线程安全的核心约束,去掉之后外部代码可以随时为AppConfig.Settings赋值新的实例,彻底破坏单例的设计原则,多线程场景下还会出现配置不一致、读写冲突等问题,稳定性风险极高。
内容的提问来源于stack exchange,提问作者Rowan Smith
相关产品推荐
相关产品推荐

