【问题标题】:How to create a copy of a Data Model如何创建数据模型的副本
【发布时间】:2014-08-19 00:15:43
【问题描述】:

在我的程序中,我试图制作一个数据模型的副本,以便我可以将其设置为另一个用户控件的数据模型。

到目前为止,我已经尝试将新的数据模型设置为我想要复制的数据模型,但所做的只是将两个用户控件都指向同一个数据模型。

我所做的一个例子:

newUserControl.NewDataModel = oldUserControl.OldDataModel;

如何制作数据模型的副本,以便我可以将其设置为另一个用户控件的数据模型上下文,而不会使 UC 瞄准相同的数据模型?

【问题讨论】:

    标签: c# wpf mvvm datamodel


    【解决方案1】:

    您在创建新 DataModel 的类上创建一个 Clone() 方法,并复制您需要的属性。

    public DataModel Clone() {
        return new DataModel() {
            PropertyA = this.PropertyA,
            PropertyB = this.PropertyB,
            //etc.
        }
    }
    
    //OR - Without a clone method:
    newUserControl.NewDataModel = new DataModel()
    {
        PropertyA = oldUserControl.OldDataModel.PropertyA,
        PropertyB = oldUserControl.OldDataModel.PropertyB,
        //etc
    }
    

    详细说明: 这不起作用的原因是,简单地将 NewDataModel 分配给 OldDataModal 的值意味着仅复制对该对象的引用。换句话说,您并没有创建新的 DataModel,您只是将两个属性指向同一个实例。

    通过创建 Clone() 方法,您将创建一个与旧 DataModel 相同的新 DataModel,但位于内存中的不同位置。

    【讨论】:

    • 几个问题:使用这种方法,我是否必须手动复制每个属性?另外,我不能按照newUserControl.newDataModel = new DataModel()... 的方式做一些事情吗?
    • 是的,是的。 Clone() 方法只是一种标准化的方法,以防您多次需要它。但是,是的,使用 new 关键字并复制所有属性(或至少是您需要的属性),一切就绪。如果您发现自己经常这样做,您可以考虑使用反射来避免手动操作,但这很快就会变得复杂。使用非克隆解决方案更新了答案。
    • 好的,在使用带有new 关键字的方法时,我发现一些属性复制正确,但有些最终仍然复制引用。例如:string 属性复制正确,但ObservableCollection 属性只能通过引用复制。
    • 是的,如果你想要一个副本,引用类型(除了简单类型之外的任何东西)都需要重新实例化。您遇到的问题是深拷贝与浅拷贝 (en.wikipedia.org/wiki/Object_copy)。如果采用这种方法,您将需要克隆出整个对象图。您可以尝试使用 evanb 的方法自动执行此操作,但请记住,您可能会遇到循环引用问题。这完全取决于您在系统中遇到此问题的次数。
    【解决方案2】:

    我要做的是实现一个消息传递基础架构。这是我以前使用过的一种,它使事情变得容易得多。它本质上是一个事件,您可以在一个控件上设置,然后在另一个控件上订阅该事件,并使用该事件来回传递内容:

    http://mvvmlight.codeplex.com/

    【讨论】:

      【解决方案3】:

      一种方法是创建一个通用扩展方法。这允许您克隆任何类型的对象,只要它是可序列化的(具有“Serializable”属性)。

      public static class ObjectExtensions
      {    
          public static T Clone<T>(this T source)
          {
                  if (!typeof(T).IsSerializable)
                  {
                      throw new ArgumentException("This type must be serializable.", "source");
                  }
      
                  if (Object.ReferenceEquals(source, null))
                      return default(T);
      
                  IFormatter formatter = new BinaryFormatter();
                  Stream stream = new MemoryStream();
                  using (stream)
                  {
                      formatter.Serialize(stream, source);
                      stream.Seek(0, SeekOrigin.Begin);
                      return (T)formatter.Deserialize(stream);
                  }
           }
      }
      

      正如@TroelsLarsen 提到的,存在复制事件订阅的风险。为避免这种情况,您可以将NonSerializedAttribute 添加到您不想序列化的字段中。这是带有示例的MSDN 文档。

      然后你就这样使用它:

      newUserControl.DataModel = oldDataUserControl.DataModel.Clone();
      

      【讨论】:

      • 这要求对象是可序列化的。此外,在处理直接绑定到 UI 的对象时,存在复制事件订阅的风险。
      • @TroelsLarsen 是的,它确实要求它是可序列化的,正如我在答案中提到的那样。您可以随时将NonSerialized 属性添加到您不想序列化的字段中。
      • 如何将NonSerialized 属性添加到我的数据模型?
      • @Ericafterdark 我在对 MSDN 文档的回答中添加了一个链接,其中包含一个示例。但基本上你只需将 [NonSerialized()] 添加到字段中,就像将 [Serializable()] 添加到类中一样。
      猜你喜欢
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-23
      • 1970-01-01
      • 1970-01-01
      • 2017-02-19
      • 1970-01-01
      相关资源
      最近更新 更多