Monday, May 21, 2012

Shallow Copy and Deep Copy in C#

When copying one instance to another there are several ways of doing it. If we try to assign an object to another object, only reference gets copied and hence both objects will point to same memory.

Obj a = new  Obj();
Obj b = a; //a and b will have same reference

Hence if you modify anything in one object will modify another. It is because in memory it is same memory, same reference assigned to two variables.

Shallow Copy: 


Shallow copy is the way copying an object's value type fields bit by bit into target object and object's reference types are copied as references into the target object but not the referenced object itself. This can be done in C# using MemberwiseClone() method on an object. As in 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."

This cloned object will contain all the values of value types that are in source object and references are copied for reference types that are in source object. i.e. copying all fields that value type bit by bit copy and for all reference type fields only references are copied but not the referred object. Lets look into below example:


    class A:ICloneable
    {
        public string i = "";
        public B b;

        #region ICloneable Members

        public object Clone()
        {
            return this.MemberwiseClone();
           
        }

        #endregion
    }


 class B
    {
        public string j = "";
    }

With above two classes let's try to do a shallow copy(see inline comments to understand what's happening in each step)


            A a = new A(); //new instance creation of type A
            a.b = new B(); //instance for b which is part of object a. This is reference type that is in A
            a.b.j = "test"; //assign values for variables inside instance b which is contained in instance a
            a.i = "test"; //i is the value type here, assigning the value to the value type inside A

            A a1 = a.Clone() as A;        //Shallow copy using this.MemberwiseClone() in A's instance 

            a1.i = "asas"; //Change the value in cloned copy for value type
            a1.b.j = "changed"; //change value in cloned copy for a variable inside reference type of A's instance

Now what we observe in above steps:

a. When we clone a to a1, all the values of a are copied to a1 including variable 'j' which is reference type b
i.e. after cloning a1.i will have value 'test' and a.b.j will have value 'test'.

b. To prove references are copied for reference type not just value copy, we will change the value a1.b.j to some other string "changed". This changes the value of a.b.j as well. This is because when clone is created , all members are cloned and references are copied as it is. Hence a.b has reference as a1.b.


Deep Copy: 

Deep copy is the way of copying an object completely bit-by-bit i.e. Deep copy copies value of every field inside an object. For nested objects(objects that contain other objects), deep copy creates new instances for each object inside nested object and copies all the values for fields inside them. 

In short, after deep copy copied instance will have same values that were in source object but none of them refer to same memory or hold same reference.

Deep copy can be performed using MemberwiseClone() method also. For each object contained in current object do a memberwise clone and copy all the values of all fields to perform a deep copy.

class A:ICloneable
    {
        public string i = "";
        public B b;

        #region ICloneable Members
        
        //This method performs deep copy
        public object Clone()
        {
            A a = this.MemberwiseClone() as A; //creating a new instance of A to copy values to
            a.b = this.b.Clone() as B; //copy each value or clone for each instance inside instance b
            return a; //return the copied object
           
        }

        #endregion
    }

    class B
    {
        public string j = "";

         public object Clone()
        {
            return this.MemberwiseClone();
            
        }
    }

In above example Clone() method of class A performs deep copy by first calling this.MemberwiseClone() on this. This step assures all value types inside are copied into new instance i.e. a inside clone method. For reference type B inside class A, we need to create another instance which copies instance b that is contained  in object a. This can be done calling memberwise clone on instance b which is in a. This call assures all the value types inside b are copied into new instance and this new instance can be assigned to the instance which we will return as deep copied instance. 

Note: Since class B did not have any reference type MemberwiseClone call in B works well for deep copy.


Deep copy implementation without using nested MemberwiseClone()


However in real time scenarios, each instance is a composition of many instances in turn might hold many instances. Hence to do a deep copy using MemberwiseClone() requires nested cloning of each instance contained and in the same hierarchy as the source object. This grows in complexity with dynamic binding and run time instance creation. It also required that we implement Deep copy for each class inside the source type. 

This complexity can be overcome by using Serialization and Deserialization to create a deep copy instance. Only classes that are marked with [Serializable] attribute can only be copied using this technique. But remember this simple way of deep copy comes with the overhead of Serialization and Deserialization and hence might impact performance.




Below is the example from stackoverflow:


public static T DeepClone<T>(T obj)
{
 using (var ms = new MemoryStream())
 {
   var formatter = new BinaryFormatter();
   formatter.Serialize(ms, obj);
   ms.Position = 0;
   return (T) formatter.Deserialize(ms);
 }
}

Steps involved are:

  •  Serialize the source object into a memory stream
  •  Set the position to starting of the stream.
  •  Deserialize this memory stream , type cast it to source object type and return as deep copy 


No comments:

Post a Comment