Kevin Meredith
Kevin Meredith

Reputation: 41939

Deep Copy in C#

MSDN gives this example of a deep copy (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;
    }
}

But, doesn't a new Person object have to be instantiated, and then returned? For example, is this code below acceptable/equal/inferior to the code above for performing a deep copy?

As I understand the MemberwiseClone() method, it just performs a shallow copy, i.e. copies values/references from copied object to new object. This results in a shallow copy since the memory references are equal, i.e. the references point to the same objects.

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

Upvotes: 13

Views: 26090

Answers (9)

Berend Engelbrecht
Berend Engelbrecht

Reputation: 1488

I liked the answer of Matthias because it is most generic. But I could not apply it to my class because the type is not serializable in .NET. To work around it, I used Newtonsoft.Json, that has a more capable object serializer:

  public static class ObjectExensions
  {
    public static T Copy<T>(this T source)
    {
      return JsonConvert.DeserializeObject<T>
        (JsonConvert.SerializeObject(source));
    }
  }

Upvotes: 0

anayarojo
anayarojo

Reputation: 1205

Other alternative as object extension is the next:

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

For use:

User lObjUserCopy = lObjUser.Copy();

Upvotes: 0

Matthias
Matthias

Reputation: 16209

Alternatively, if you are able to set Serializable attribute to all involved classes, you can use serialization. For the purpose of a generic deep copy I have this object extension method:

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
}

This should also copy your IdInfo field.

Usage is simple:

var copy = obj.Copy();

Upvotes: 7

ChrisWue
ChrisWue

Reputation: 19060

MemberwiseClone does create a new object and copies all non static fields. In case of reference types this means copying the references. So yes, after a member wise clone the fields of the new object point to the same objects as the fields of the original object (for reference types). That is why the example in the MSDN create a new instance of the IdInfo: To create a copy of that object as well.

Your DeepCopy implementation is broken because it does not copy all fields, but if it would then the result would not be different from the MemberwiseClone solution.

This question might also be an interesting read for you: Create a Deep Copy in C#

Upvotes: 4

hypermush
hypermush

Reputation: 446

Your DeepCopy will not copy the Age and Name fields from the object being copied. They will get their default(T) values instead (Age = 0, Name = null).

MemberwiseClone does create a new object just like you did, but it also copies the fields:

Person other = new Person();

other.Age = this.Age;

other.Name = this.Name;

As int is a value type it will be copied to the new object. The Name field will reference the same string that was referenced by Name - if that is not okay then you would need to Clone() that and set the reference in your DeepCopy() method just like IdInfo.

According to MSDN: The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object.

Upvotes: 0

Tomislav Markovski
Tomislav Markovski

Reputation: 12366

The implementation of MemberwiseClone would do the following for your code.

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;

Upvotes: 3

MatRush
MatRush

Reputation: 23

Your new method does not copy the value of this.Age and this.Name, so i think it's even not called copy.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726997

The MemberwiseClone() creates a new instance of the class being copied, and also copies scalar fields into the corresponding members of the copy. It provides a better starting point for deep copying than just a plain new, because you need to "fix" only the items requiring deep copying.

Upvotes: 8

Adriaan Stander
Adriaan Stander

Reputation: 166556

In the example that you specified, the values of Age and Name would be zero/blank.

This is due to the fact that you instantiate the Person object, but never set the values of these fields.

From Object.MemberwiseClone Method

The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.

So as you can see, using the MemberwiseClone method, your Age/Name fields will also be copied/cloned.

Upvotes: 14

Related Questions