C#中的深层复制

时间:2011-12-28 04:05:12

标签: c#

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;
    }
}

8 个答案:

答案 0 :(得分:12)

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

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

来自Object.MemberwiseClone Method

  

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

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

答案 1 :(得分:6)

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

答案 2 :(得分:6)

或者,如果您能够将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();

答案 3 :(得分:4)

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

您的DeepCopy实施已被破坏,因为它不会复制所有字段,但如果是,则结果与MemberwiseClone解决方案不同。

这个问题也可能是一本有趣的读物:Create a Deep Copy in C#

答案 4 :(得分:3)

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;

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

Person other = new Person();

other.Age = this.Age;

other.Name = this.Name;

由于int是值类型,因此它将被复制到新对象。 Name字段将引用Name引用的相同字符串 - 如果不正常则需要克隆()并在DeepCopy()方法中设置引用,就像IdInfo一样。

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

答案 7 :(得分:0)

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

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();