You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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足够满足需求。

注意事项

  1. UI线程操作:如果是WPF/UWP项目,ObservableCollection的变更必须在UI线程执行,否则会报错。可以用Application.Current.Dispatcher.Invoke来包裹操作。
  2. 复杂对象对比:如果ManifestJSON包含子对象(引用类型),需要递归对比子属性,或者让子对象也实现INotifyPropertyChanged。
  3. 性能优化:如果集合很大,每次全量对比会有性能问题,可以考虑在API返回时带上变更标记(比如LastModified时间),只同步变化的项。

内容的提问来源于stack exchange,提问作者Dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:26:15