C#中如何检测反序列化对象属性变更以更新集合
解决ObservableCollection直接覆盖的属性变更追踪问题
这个场景我之前做定时同步数据时也踩过坑,直接赋值覆盖集合不仅会丢失UI绑定的连续性,还没法追踪单个项的属性变化。给你几个实用的解决方案,按实现复杂度从低到高排序:
方案1:手动对比新旧集合,处理增删改
这是最直接的思路,核心是通过唯一标识匹配新旧数据项,然后分别处理新增、删除、更新操作:
前提准备
首先确保你的ManifestJSON实体有一个唯一标识(比如Id、Guid或者业务上唯一的字段),如果原类没有,建议添加一个——没有唯一标识的话,根本没法准确匹配新旧项。
代码示例
// 假设你已经获取到API返回的JSON字符串,先反序列化为临时列表 var newManifestList = JsonConvert.DeserializeObject<List<ManifestJSON>>(apiResponse); var currentItems = myObservableCollection.ToList(); // 1. 处理新增项:添加新集合独有的项 var itemsToAdd = newManifestList .Where(newItem => !currentItems.Any(curr => curr.UniqueId == newItem.UniqueId)) .ToList(); foreach (var item in itemsToAdd) { myObservableCollection.Add(item); } // 2. 处理删除项:移除旧集合独有的项 var itemsToRemove = currentItems .Where(curr => !newManifestList.Any(newItem => newItem.UniqueId == curr.UniqueId)) .ToList(); foreach (var item in itemsToRemove) { myObservableCollection.Remove(item); } // 3. 处理更新项:对比属性,只更新有变化的字段 var itemsToUpdate = newManifestList .Where(newItem => currentItems.Any(curr => curr.UniqueId == newItem.UniqueId)) .ToList(); foreach (var newItem in itemsToUpdate) { var existingItem = currentItems.First(curr => curr.UniqueId == newItem.UniqueId); // 手动对比属性(如果属性多的话,建议用反射或AutoMapper简化) if (existingItem.Name != newItem.Name) existingItem.Name = newItem.Name; if (existingItem.Version != newItem.Version) existingItem.Version = newItem.Version; // ... 其他属性依次对比更新 }
优化技巧
如果实体属性很多,手动写对比太繁琐,可以用AutoMapper来自动映射变化的属性:
// 先配置AutoMapper(全局配置一次即可) Mapper.Initialize(cfg => cfg.CreateMap<ManifestJSON, ManifestJSON>()); // 更新时直接映射 Mapper.Map(newItem, existingItem);
只要你的ManifestJSON实现了INotifyPropertyChanged,AutoMapper会自动触发属性变更通知,UI会同步更新。
方案2:让实体实现INotifyPropertyChanged,复用现有实例
这个方案更优雅,核心是不创建新的实体实例,而是把新数据的属性值赋值给现有实例:
步骤1:给ManifestJSON实现INotifyPropertyChanged
public class ManifestJSON : INotifyPropertyChanged { private string _name; public string Name { get => _name; set { if (_name != value) { _name = value; OnPropertyChanged(); } } } // 其他属性同理... public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
步骤2:同步数据时复用实例
var newManifestList = JsonConvert.DeserializeObject<List<ManifestJSON>>(apiResponse); var currentItems = myObservableCollection.ToList(); // 先处理更新和新增 foreach (var newItem in newManifestList) { var existingItem = currentItems.FirstOrDefault(curr => curr.UniqueId == newItem.UniqueId); if (existingItem != null) { // 用AutoMapper或手动赋值更新属性 Mapper.Map(newItem, existingItem); } else { myObservableCollection.Add(newItem); } } // 再处理删除 var itemsToRemove = currentItems .Where(curr => !newManifestList.Any(newItem => newItem.UniqueId == curr.UniqueId)) .ToList(); foreach (var item in itemsToRemove) { myObservableCollection.Remove(item); }
方案3:使用增强型集合(可选)
如果不想自己写对比逻辑,可以考虑用第三方库的增强型集合,比如:
- ReactiveUI的ReactiveList:支持批量更新、变更通知,还能自动处理集合和属性的变更追踪。
- Prism的ObservableCollectionExtensions:提供了批量更新的扩展方法,简化增删改操作。
不过如果项目不想引入额外依赖,方案1或2足够满足需求。
注意事项
- UI线程操作:如果是WPF/UWP项目,ObservableCollection的变更必须在UI线程执行,否则会报错。可以用
Application.Current.Dispatcher.Invoke来包裹操作。 - 复杂对象对比:如果
ManifestJSON包含子对象(引用类型),需要递归对比子属性,或者让子对象也实现INotifyPropertyChanged。 - 性能优化:如果集合很大,每次全量对比会有性能问题,可以考虑在API返回时带上变更标记(比如LastModified时间),只同步变化的项。
内容的提问来源于stack exchange,提问作者Dev
相关产品推荐
相关产品推荐

