If you’ve ever needed to make a copy of an object in C#, you might have come across the MemberwiseClone() method. This can save you a lot of hassle of left-hand/right-hand property matching, but it does come with some “gotchas” that might not be readily apparent if you haven’t carefully read the documentation.
Let’s start with a class that looks like this. Notice that it has a ShallowCopy method that just returns a MemberwiseClone of the object.
public class Person
{
public string Name { get; set; }
public Person Boss { get; set; }
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
}
Now, let’s create some objects and make a shallow copy of the worker and see if the objects are the same:
Person bigBoss = new Person() { Name = "Tony", Boss = null };
Person boss = new Person() { Name = "Silvio", Boss = bigBoss };
Person worker = new Person() { Name = "Paulie", Boss = boss };
Person cloneWorker = worker.ShallowCopy();
Console.WriteLine("Are the workers the same? {0}", worker.Equals(cloneWorker));
Console.WriteLine("Are the bosses the same? {0}", worker.Boss.Equals(cloneWorker.Boss));
This gives us the following results:
Are the workers the same? False
Are the bosses the same? True
“So what?” you might ask. Well, what that means is that if I change a property on the Boss of either object, it automatically changes in the other object (since only the reference was copied, and that reference points to the same memory address in both cases).
worker.Boss.Name = "Chuck";
Console.WriteLine("Worker's Boss' Name: {0}.", worker.Boss.Name);
Console.WriteLine("Clone Worker's Boss' Name: {0}.", cloneWorker.Boss.Name);
This gives us the output:
Worker's Boss' Name: Chuck.
Clone Worker's Boss' Name: Chuck.
It is possible that you may be okay with this happening. However, if you don’t want this to happen, you can implement a DeepCopy on your object instead. In the DeepCopy, you take a MemberwiseClone and then you call for a deep copy on every reference type in the object. In our example, I’d add this to our Person class:
public Person DeepCopy()
{
Person copy = (Person)this.MemberwiseClone();
if (this.Boss != null)
{
copy.Boss = this.Boss.DeepCopy();
}
return copy;
}
Now, when I check on the objects, they aren’t the same. And, when we change the property on the boss, it doesn’t automatically change in both places because they are completely separate objects.
Are the workers the same? False
Are the bosses the same? False
Worker's Boss' Name: Chuck.
Clone Worker's Boss' Name: Silvio.
That’s all there is to it. I have wrapped up the code for this blog post into a small console project and put it on GitHub. If you have any questions, let me know in the comments.