#830 – The Problem with ICloneable

The ICloneable interface in the .NET Framework includes a single Clone method, meant to make a copy of the object it is invoked on, returning the new copy.

The problem with the ICloneable interface is that it gives client code that uses it no information about whether a deep or a shallow copy (or something in between) is going to be done.  Given the ambiguity and the fact that the client doesn’t really know what the contents of the cloned object will be, it’s recommended that you don’t implement ICloneable, but rather design your own interface or methods, where you can make the semantics more clear.

 

Advertisement

#828 – Implementing Both a Copy Constructor and ICloneable

A copy constructor is a constructor that creates a new object by making a copy of an existing object.  ICloneable is a standard interface that you can implement, whereby you’ll add a Clone method to your class.  The purpose of the Clone method is to make a copy of the existing object.

Neither a copy constructor nor the ICloneable interface dictates whether you make a shallow or a deep copy of an object.

Your class can implement both methods for making a copy of an object.

    public class Dog : ICloneable
    {
        public string Name { get; set; }
        public int Age { get; set; }

        // Standard constructor
        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

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

        // ICloneable
        public object Clone()
        {
            return new Dog(this);
        }
    }

#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.

#141 – Implementing ICloneable for a Custom Type

A type can choose to implement the ICloneable interface, which includes the single Clone method.  The intent of the Clone method is to create a copy of the object.

The simplest way to create a copy of the object is to call the Object.MemberwiseClone method.

        public class Person : ICloneable
        {
            public string LastName { get; set; }
            public string FirstName { get; set; }
            public Person(string lastname, string firstname)
            {
                LastName = lastname;
                FirstName = firstname;
            }

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

If your intention is to make a deep copy and your type contains only members that are value types, this is sufficient.  However, if the type contains members that are reference types, you’ll have to do more in order to get a deep copy.