反序列化替换AppSettings实例时如何保留原有全部引用不失效?
解决方案
方案1:全局单例+属性变更通知(WPF场景首选)
这是最适配当前场景的方案,从根源上避免引用失效,同时兼容WPF数据绑定逻辑:
- 把
AppSettings改造为全局单例,全应用生命周期内仅保留唯一实例,永远不直接替换该实例对象 - 给
AppSettings和所有嵌套自定义类(比如你的Unit类)实现INotifyPropertyChanged接口,属性赋值时触发变更通知 - 调整反序列化逻辑:反序列化得到的结果仅作为临时数据源,把临时实例的所有属性值逐一写入已有的单例实例,而不是直接替换单例本身
你提到的手动维护赋值逻辑成本高的问题,实际场景中配置类的迭代频率极低,手写赋值逻辑的维护成本可以忽略。如果不想手动维护,用反射实现深度属性拷贝完全可行:配置加载属于极低频操作,一般仅在应用启动、用户手动导入配置时执行一次,哪怕遍历几十层嵌套属性,反射的耗时也不到1ms,不存在性能瓶颈。
示例代码
单例带通知的AppSettings实现
<Serializable()> Public Class AppSettings Implements INotifyPropertyChanged ' 全局唯一实例 Private Shared _instance As New AppSettings() Public Shared ReadOnly Property Instance As AppSettings Get Return _instance End Get End Property ' 私有构造函数,禁止外部创建新实例 Private Sub New() End Sub Private _windowHeight As Integer = 600 Public Property windowHeight As Integer Get Return _windowHeight End Get Set(value As Integer) _windowHeight = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(windowHeight))) End Set End Property Private _windowWidth As Integer = 800 Public Property windowWidth As Integer Get Return _windowWidth End Get Set(value As Integer) _windowWidth = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(windowWidth))) End Set End Property Private _defaultLengthUnit As Unit = Units.meters Public Property defaultLengthUnit As Unit Get Return _defaultLengthUnit End Get Set(value As Unit) _defaultLengthUnit = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(defaultLengthUnit))) End Set End Property Private _defaultAngleUnit As Unit = Units.degrees Public Property defaultAngleUnit As Unit Get Return _defaultAngleUnit End Get Set(value As Unit) _defaultAngleUnit = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(defaultAngleUnit))) End Set End Property Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged End Class
带通知的Unit类实现
<Serializable()> Public Class Unit Implements INotifyPropertyChanged Private _name As String Public Property Name As String Get Return _name End Get Set(value As String) _name = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Name))) End Set End Property Private _abbreviation As String Public Property Abbreviation As String Get Return _abbreviation End Get Set(value As String) _abbreviation = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Abbreviation))) End Set End Property Private _scale As Double Public Property Scale As Double Get Return _scale End Get Set(value As Double) _scale = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Scale))) End Set End Property Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged End Class
修改后的反序列化逻辑
Public Sub LoadSettingsFromFile(ByVal filename As String) If Not File.Exists(filename) Then Return ' 反序列化得到临时实例 Dim tempSettings As AppSettings Using sr As StreamReader = New StreamReader(filename) Dim xmls As XmlSerializer = New XmlSerializer(GetType(AppSettings)) tempSettings = TryCast(xmls.Deserialize(sr), AppSettings) End Using If tempSettings Is Nothing Then Return ' 把临时实例的属性更新到单例,引用类型只更新内部属性不替换实例 With AppSettings.Instance .windowHeight = tempSettings.windowHeight .windowWidth = tempSettings.windowWidth .defaultLengthUnit.Name = tempSettings.defaultLengthUnit.Name .defaultLengthUnit.Abbreviation = tempSettings.defaultLengthUnit.Abbreviation .defaultLengthUnit.Scale = tempSettings.defaultLengthUnit.Scale .defaultAngleUnit.Name = tempSettings.defaultAngleUnit.Name .defaultAngleUnit.Abbreviation = tempSettings.defaultAngleUnit.Abbreviation .defaultAngleUnit.Scale = tempSettings.defaultAngleUnit.Scale End With End Sub
所有原有引用都指向同一个单例实例,属性更新后WPF绑定会自动收到通知刷新,完全不会出现引用断裂问题。
方案2:静态代理包装(最小改动现有代码)
如果你不想改动现有所有引用AppSettings的业务逻辑,可以加一层静态代理层,所有对配置的访问都走代理,反序列化时只需要替换代理内部持有的实例即可:
Public NotInheritable Class AppSettingsProxy Private Shared _current As AppSettings = AppSettings.Instance ' 禁止外部实例化 Private Sub New() End Sub Public Shared Property Current As AppSettings Get Return _current End Get Set(value As AppSettings) _current = value End Set End Property End Class
原有代码中所有直接访问AppSettings的地方改为AppSettingsProxy.Current,反序列化后直接给AppSettingsProxy.Current赋值新实例即可,所有访问会自动指向新实例。注意该方案下WPF绑定路径需要同步修改为代理的属性,否则绑定仍会失效。
内容的提问来源于stack exchange,提问作者Justin8051
相关产品推荐
相关产品推荐

