Clone objects

Today a collegue asked for a clean way to clone objects in C#,  so that you really have different objects instead of a reference copy . So imagine that you have a class Employee as follows:

public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
}

and you would copy it:

Employee original = new Employee()
{
FirstName = "Homer",
LastName = "Simpson",
};
Employee copy = original;
copy.FirstName = "Marge";
Console.WriteLine(string.Format("{0} {1}",
original.FirstName, original.LastName));
Console.WriteLine(string.Format("{0} {1}",
copy.FirstName, copy.LastName));

Changing the first name of the copied object obviously also changes the first name of the original object, because it has been copied by refererence and so original and copy refer to the same object:
 

And this was not wat we wanted.
To solve it, we first write an extension method that will be available on each class that implements the ICloneable interface:
 
static class Extensions
{
public static T Clone<T>(this T objectToClone) where T : ICloneable
{
return (T)objectToClone.Clone();
}
}
 
Next we have to make the Employee class implement the ICloneable interface:
 
public class Employee : ICloneable
{
public string FirstName { get; set; }
public string LastName { get; set; }

#region ICloneable Members
public object Clone()
{
return new Employee() {FirstName = FirstName, LastName = LastName};
}
  #endregion
}

And if we now use the Clone method like:
 
Employee copy = (Employee)original.Clone();

then the new object is really a copy of the original:
 

If you want to clone a list of objects, add an extension method like this:

static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone)
where T : ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}

and to clone this list is then very simple:

IList<Employee> copiedList = originalList.Clone();

This example clones only the Employee object (known as shallow copy, see here), but what if the Employee class contains other reference types? In this case we have to implement a deep copy, meaning that every subclass has to implement ICloneable too, for example, if you have a City:

public class City: ICloneable
{
public string CityName { get; set; }
#region ICloneable Members
public object Clone()
{
return new City() { CityName = CityName };
}
#endregion
}

then Employee has to clone it like:

public class Employee : ICloneable
{
public string FirstName { get; set; }
public string LastName { get; set; }
public City City{ get; set; }
#region ICloneable Members
public object Clone()
{
return new Employee() {FirstName = FirstName,
LastName = LastName, City = (City)City.Clone() };
}
#endregion
}

We can do one other improvement. As you see we always have to cast the Clone operation to its actual type. To solve  that we can create a generic ICloneable interface:

interface ICloneable<T> : ICloneable
{
new T Clone();
}

and adapt the classes to it:

public class Employee : ICloneable<Employee>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public City City{ get; set; }
#region ICloneable<Employee> Members
Employee ICloneable<Employee>.Clone()
{
return new Employee() { FirstName = FirstName,
LastName = LastName, City = City.Clone() };
}
#endregion

#region ICloneable Members
object ICloneable.Clone()
{
return new Employee() { FirstName = FirstName,
LastName = LastName, City = City.Clone() };
}
#endregion
}

public class City : ICloneable<City>
{
public string CityName { get; set; }
#region ICloneable<City> Members
public City Clone()
{
return new City() { CityName = CityName };
}
#endregion

#region ICloneable Members
object ICloneable.Clone()
{
return new City() { CityName = CityName };
}
#endregion
}

Advertisements

3 thoughts on “Clone objects

  1. DickB says:

    Thanks a lot, Ludwig!
    Very elegant solution to my problem.

  2. Kreso says:

    A potential drawback is a requirement for the object graph to implement the IClonable interface in order to achieve a deep copy.

    In cases where you have no control over the object graph you’d like to deep copy, a serialization may be an option.

    var serializer = new XmlSerializer(typeof(Employee));
    using (var stream = new MemoryStream())
    {
    serializer.Serialize(stream, employeeA);
    stream.Position = 0;
    employeeB = (Employee)serializer.Deserialize(stream);
    }

    • Ludwig Stuyck says:

      Thanks! Indeed, it could be a solution in these cases, if the object can be serialized of course 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s