【问题标题】:ObservableCollection deep cloningObservableCollection 深度克隆
【发布时间】:2021-04-20 16:58:10
【问题描述】:

我已经实现了 ObservableCollection 的深度克隆,以便通过取消按钮将项目重置为可编辑 Datagrid 中的原始状态。

为此,我有两个集合 - 一个 ObservableCollection 将 Datagrid 绑定到它,并克隆 List 以在需要时将 ObservableCollection 重新初始化为它的原始状态。

我的代码仅在我第一次点击取消按钮时才有效,之后我的克隆列表也发生了变化。

提供的代码是一个示例(我的有点长),但它与我的 100% 相同:

实现ICloneable的模型:

public class EmployeeModel : ICloneable
{
   public object Clone()
   {
       return MemberwiseClone();
   }
    
   public string NAME
   {
      get { return _name; }
      set
         {
            if (_name != value)
            {
               CHANGE = true;
                _name = value;
             }
          }
     }
     private string _name;

     public string SURNAME
     {
        get { return _surname; }
        set
         {
            if (_surname != value)
            {
               CHANGE = true;
                _surname = value;
             }
          }
     }
     private string _surname; 

     ///<summary>Property for tracking changes in model</summary>
     public bool CHANGE { get; set; }
}

视图模型:

public ViewModel() : Base //Implements InotifyPropertyChanged
{
   public ViewModel()
   {
      Task.Run(()=> GetData());
   }

   public ObservableCollection<EmployeeModel> Employees
   {
       get { return _employees; }
       set { _employees = value; OnPropertyChanged();}
   }
   private ObservableCollection<EmployeeModel> _employees;

   public List<EmployeeModel> Copy_employees
   {
        get { return _copy_employees; }
        set { _copy_employees = value; OnPropertyChanged();}
   }
   private List<EmployeeModel> _copy_employees;

   //Fetch data from DB
   private async Task Get_data()
   {
       //Returns new ObservableCollection of type Employee
       Employees = await _procedures.Get_employees();

       if (Employees != null) //Now make a deep copy of Collection
       {
            Copy_employees = new List<EmployeeModel>();
            Copy_employees = Employees.Select(s => (EmployeeModel)s.Clone()).ToList();
       }
    }

   //My Command for canceling changes (reseting DataGrid)
   //CanExecute happens, when model is changed - tracking via CHANGE property of EmployeeModel
   public void Cancel_Execute(object parameter)
   {
        Employees.Clear(); //Tried with re-initializing too, but same result
        
        foreach (var item in Copy_employees)// Reset binded ObservableCollection with old items
        {
             Employees.Add(item);
        }
         
        //Check if copied List really hasn't got any changes                    
        foreach (EmployeeModel item in Copy_employees)
        {
           Console.WriteLine("Changes are " + item.CHANGES.ToString());
        }

     }
 }

取消命令的输出:

1.) 我第一次点击取消按钮:

// Changes are False

每次:

// Changes are True

所以,正如我从控制台看到的那样,当 ObservableColection 更新时,我复制的列表也会更新,即使它没有绑定到 DataGrid。 它只更新我更改的属性,因此 List 反映 ObservableCollection 项。

我怎样才能保留我的原始项目 List&lt;Employee&gt;并随时将它们复制到绑定的 ObservableCollection 中?

【问题讨论】:

  • 不使用memberwiseclone。这会复制对对象的引用。看一眼。 .stackoverflow.com/questions/78536/deep-cloning-objects 个人而言,我通常使用 automapper 将 dto 复制到视图模型中。
  • @Andy:显示的代码仅包含 EmployeeModel 对象中的不可变字段,因此虽然一般来说 MemberwiseClone() 不会进行深层复制是对的,但实际上在这里没关系。
  • 您发布的代码没有显示任何形式的ToString() 覆盖EmployeeModel,因此代码不可能输出任何类似于"Changes are False" 的文本。缺少正确的minimal reproducible example,不可能知道实际发生了什么,因此无法为这个问题提供好的答案。
  • @PeterDuniho,我刚刚编辑了它,没看到,抱歉。
  • @Andy,我已经在您提供的链接中尝试了接受的答案,但它不能在 C# 中编译。

标签: wpf observablecollection deep-copy


【解决方案1】:

当您返回值时,您不会返回它们,而是将支持项引用写入可编辑集合。 因此,您在两个集合中都有相同的实例。 在最简单的情况下,当你返回它们时,你也需要克隆。

public void Cancel_Execute(对象参数) { 雇员.清除(); //也尝试过重新初始化,但结果相同

    foreach (var item in Copy_employees)// Reset binded ObservableCollection with old items
    {
         Employees.Add((EmployeeModel)item.Clone());
    }
     
    //Check if copied List really hasn't got any changes                    
    foreach (EmployeeModel item in Copy_employees)
    {
       Console.WriteLine("Changes are " + item.CHANGES.ToString());
    }

 }

与问题无关,但我仍然建议您使用对可克隆用户更友好的界面:

public interface ICloneable<T> : ICloneable
{
    new T Clone();
}

【讨论】:

  • 非常感谢,这几天我都想不通。虽然我仍然很困惑 - 当我退回物品时,我需要再次克隆它们。如果它们不是第一次克隆而没有可编辑集合的项目引用,那么为什么第二次克隆有效?
  • 感谢界面。但我决定放弃 ICloneable 接口并坚持使用 Andy 提出的方案——使用序列化深度克隆对象的解决方案。至少这是一种更简洁的代码。再次感谢!!!
  • 如果您不是每次都克隆,那么您将获得指向不同集合中相同实例的链接。为了正确地工作,您需要两个具有不同但相同样本的集合。
  • 序列化,在这个任务中,在所需资源方面是非常多余和不合理的。 ICloneableICloneable &lt;T&gt; 通过 MemberwiseClone() 实现在这里就足够了。当克隆对象包含对引用类型的其他可变实例的引用时,使用序列化。您的实体类型 EmployeeModel 中没有此类引用。即使它们是,类型本身也必须知道它,并且它必须自己决定应该额外克隆什么以获得深层副本。
猜你喜欢
  • 1970-01-01
  • 2017-09-01
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 2020-03-04
相关资源
最近更新 更多