【问题标题】:Deep Copy in C#C# 中的深度复制
【发布时间】:2011-12-28 04:05:12
【问题描述】:

MSDN 给出了这个深拷贝的例子 (http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx)

public class Person 
{
    public int Age;
    public string Name;
    public IdInfo IdInfo;

    public Person ShallowCopy()
    {
       return (Person)this.MemberwiseClone();
    }

    public Person DeepCopy()
    {
       Person other = (Person) this.MemberwiseClone(); 
       other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
       return other;
    }
}

但是,不是必须实例化一个新的 Person 对象,然后返回吗?例如,下面的代码是否可以接受/等于/低于上面的代码以执行深层复制?

据我了解 MemberwiseClone() 方法,它只是执行浅拷贝,即将复制对象中的值/引用复制到新对象。这会导致浅拷贝,因为内存引用是相等的,即引用指向相同的对象。

public class Person 
{
    public int Age;
    public string Name;
    public IdInfo IdInfo;

    public Person ShallowCopy()
    {
       return (Person)this.MemberwiseClone();
    }

    public Person DeepCopy()
    {
       Person other = new Person(); // difference
       other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
       return other;
    }
}

【问题讨论】:

标签: c#


【解决方案1】:

在您指定的示例中,Age 和 Name 的值将为零/空白。

这是因为您实例化了 Person 对象,但从未设置这些字段的值。

来自Object.MemberwiseClone Method

MemberwiseClone 方法通过创建一个新的 对象,然后将当前对象的非静态字段复制到 新对象。 如果字段是值类型,则逐位复制 字段被执行。 如果字段是引用类型,则引用是 复制但引用的对象不是;因此,原始对象 及其克隆引用同一个对象。

如您所见,使用 MemberwiseClone 方法,您的年龄/姓名字段也将被复制/克隆。

【讨论】:

    【解决方案2】:

    MemberwiseClone() 创建被复制类的新实例,并将标量字段复制到副本的相应成员中。它为深度复制提供了一个比简单的new 更好的起点,因为您只需要“修复”需要深度复制的项目。

    【讨论】:

      【解决方案3】:

      或者,如果您能够为所有涉及的类设置Serializable 属性,则可以使用序列化。出于通用深拷贝的目的,我有这个object 扩展方法:

      public static class ObjectExtensions
      {
          #region Methods
      
          public static T Copy<T>(this T source)
          {
              var isNotSerializable = !typeof(T).IsSerializable;
              if (isNotSerializable)
                  throw new ArgumentException("The type must be serializable.", "source");
      
              var sourceIsNull = ReferenceEquals(source, null);
              if (sourceIsNull)
                  return default(T);
      
              var formatter = new BinaryFormatter();
              using (var stream = new MemoryStream())
              {
                  formatter.Serialize(stream, source);
                  stream.Seek(0, SeekOrigin.Begin);
                  return (T)formatter.Deserialize(stream);
              }
          }
      
          #endregion
      }
      

      这也应该复制您的 IdInfo 字段。

      用法很简单:

      var copy = obj.Copy();
      

      【讨论】:

        【解决方案4】:

        MemberwiseClone 确实会创建一个新对象并复制所有非静态字段。在引用类型的情况下,这意味着复制引用。所以是的,在成员明智地克隆之后,新对象的字段指向与原始对象的字段相同的对象(对于引用类型)。这就是为什么 MSDN 中的示例创建 IdInfo 的新实例的原因:同时创建该对象的副本。

        您的 DeepCopy 实现已损坏,因为它不会复制所有字段,但如果会复制,则结果与 MemberwiseClone 解决方案没有什么不同。

        这个问题对你来说可能也很有趣:Create a Deep Copy in C#

        【讨论】:

          【解决方案5】:

          MemberwiseClone 的实现将为您的代码执行以下操作。

          Person p = new Person();
          p.Age = this.Age;  // value copy
          p.Name = this.Name; // value copy
          p.IdInfo = this.IdInfo; // reference copy. this object is the same in both coppies.
          return p;
          

          【讨论】:

            【解决方案6】:

            您的新方法不会复制 this.Age 和 this.Name 的值,所以我认为它甚至不称为复制。

            【讨论】:

              【解决方案7】:

              您的 DeepCopy 不会从正在复制的对象中复制年龄和姓名字段。他们将获得默认 (T) 值(Age = 0,Name = null)。

              MemberwiseClone 像你一样创建一个新对象,但它也会复制字段:

              Person other = new Person();

              other.Age = this.Age;

              other.Name = this.Name;

              由于 int 是一种值类型,它将被复制到新对象中。 Name 字段将引用 Name 引用的相同字符串 - 如果这不合适,那么您需要 Clone() 并在 DeepCopy() 方法中设置引用,就像 IdInfo 一样。

              根据MSDN:MemberwiseClone 方法通过创建一个新对象来创建浅拷贝,然后将当前对象的非静态字段复制到新对象中。

              【讨论】:

                【解决方案8】:

                作为对象扩展的其他替代方法是下一个:

                public static class ObjectExtension
                {
                        public static T Copy<T>(this T lObjSource)
                        {
                            T lObjCopy = (T)Activator.CreateInstance(typeof(T));
                
                            foreach (PropertyInfo lObjCopyProperty in lObjCopy.GetType().GetProperties())
                            {
                                lObjCopyProperty.SetValue
                                (
                                    lObjCopy,
                                    lObjSource.GetType().GetProperties().Where(x => x.Name == lObjCopyProperty.Name).FirstOrDefault().GetValue(lObjSource)
                                );
                            }
                
                            return lObjCopy;
                        }
                }
                

                使用:

                User lObjUserCopy = lObjUser.Copy();
                

                【讨论】:

                  猜你喜欢
                  • 2015-02-28
                  • 2020-11-08
                  • 2017-04-09
                  • 1970-01-01
                  • 2011-02-28
                  • 1970-01-01
                  • 2021-12-12
                  • 1970-01-01
                  相关资源
                  最近更新 更多