#827 – Making a Deep Copy with a Copy Constructor

The semantics that you use within a copy constructor can be to make a shallow copy of an object or a deep copy.

In the example below, the copy constructor for Dog makes a deep copy of the object passed in.  In this case, that means that the new Dog that is created will also have a new instance of DogCollar object, copied from the DogCollar property of the original Dog object.

    public class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public DogCollar Collar { get; set; }

        // Constructor that takes individual property values
        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

        // Copy constructor (deep copy)
        public Dog(Dog otherDog)
        {
            Name = otherDog.Name;
            Age = otherDog.Age;

            Collar = (otherDog.Collar != null) ?
                new DogCollar(otherDog.Collar.Length, otherDog.Collar.Width) :
                null;
        }
    }

#826 – Deep Copies

When making a copy of an object, you can either make a shallow copy or a deep copy.  As opposed to a shallow copy, a deep copy is one in which a copy is made not only of the top-level object, but of all the objects referenced by the original object.

During the copy process, whenever a reference to a child object is encountered, a new instance (or deep copy) of the child object is made.

For example, if a Dog object contains a reference to a DogCollar object and we make a deep copy, we get the following:

826-001

When doing a deep copy, a deep copy is typically made of all child objects in the object hierarchy.  For example, if the DogCollar instance had in turn referred to another object, a copy would have been made of that object.

#825 – Shallow Copies

When making a copy of an object, you can either make a shallow copy or a deep copy.  A shallow copy is one in which the exact values of all data members of the object are copied.  You can think of this as byte-for-byte copy of the original object.

With a shallow copy, reference-typed members are copied by copying the reference to the object, rather than by making a new copy of the child object.

For example, if a Dog object contains a reference to a DogCollar object and we make a shallow copy, we get the following:

825-001

The problem with a shallow copy is that the second object is now referencing the sub-objects that the first object pointed to.  In many cases, this may not be what you want.  In the example above, if we change the first dog’s collar, we also change the second dog’s collar.

#266 – You Can’t Prevent a Method from Changing the Contents of Reference Types

We saw that even when you pass a variable by value to a method, if that variable is a reference to an object, the method can change the contents of the object.

C# doesn’t have a built-in language construct that allows you to tell the compiler to prohibit a method from changing the contents of an object that a parameter references.  (In C++, we can do this with the const keyword).

In C#, if you want to prevent a method from changing an object, you need to first clone your original object and pass a copy of the object to the method. (You’ll want to make a deep copy of the object).

Here’s an example of using the DeepCopy<T> method to pass a copy of the object.

            kirby.BarkAt(DeepCopy<Dog>(jack));


#144 – Using Serialization to Implement Deep Copying

One alternative to using the ICloneable interface to support making a deep copy for a custom type is to use serialization.

This method will work only if the class to be copied, as well as all classes referenced by it (directly or indirectly), are serializable.

For example, if we have a Person class that has some value-typed members, but also a reference to an instance of the Address class, both the Person and Address classes have to be serializable in order to use this technique.

Here’s a generic method that makes a deep copy using serialization, serializing the object into a stream and then deserializing into a new object.

        public static T DeepCopy<T>(T obj)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
                stream.Position = 0;

                return (T)formatter.Deserialize(stream);
            }
        }

Using this method to deep copy a Person:

            Person bronteClone = DeepCopy<Person>(emily);

#143 – An Example of Implementing ICloneable for Deep Copies

Here’s an example of implementing ICloneable in two custom classes so that you can use the Clone method to do a deep copy.

To do a deep copy of the Person class, we need to copy its members that are value types and then create a new instance of Address by calling its Clone method.

        public class Person : ICloneable
        {
            public string LastName { get; set; }
            public string FirstName { get; set; }
            public Address PersonAddress { get; set; }

            public object Clone()
            {
                Person newPerson = (Person)this.MemberwiseClone();
                newPerson.PersonAddress = (Address)this.PersonAddress.Clone();

                return newPerson;
            }
        }

The Address class uses MemberwiseClone to make a copy of itself.

        public class Address : ICloneable
        {
            public int HouseNumber { get; set; }
            public string StreetName { get; set; }

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

Cloning a Person:

            Person herClone = (Person)emilyBronte.Clone();

#142 – Implement ICloneable All the Way Down for Deep Copies

If you want to use the ICloneable.Clone method of a class to make a deep copy, you class must be implemented so that Clone provides a deep copy.  It’s up to the author of a class whether the Clone method makes a shallow or a deep copy.

If you want Clone to support deep copies and you are authoring the class, you can do one of two things:

  • If your class’ members are all value types, just use MemberwiseClone
  • Clone your object and then make a deep copy of any objects that it references

You can use Clone as a deep copy mechanism all the way down the object tree only if you are the author of each class and can implement deep copy semantics, or if you know that the objects your object references have a mechanism for making deep copies.