Watch out when cloning objects

Cloning is never a good thing. I would say in most cases there is a better alternative for reaching your goal, but now and then it happens that you need it. But in such a case, be careful. Assume you have something like this:
class Person
{
public string Name { get; set; }
public Address Address { get; set; }

public Person Clone()
{
return MemberwiseClone() as Person;
}
}

class Address
{
public string Street { get; set; }
public string Country { get; set; }
}
Note, to clone I have to use the inherited method "MemberwiseClone()" from Object and expose it publicly. This creates a shallow copy, meaning copying just the surface. So if you then have somewhere
Person p = new Person
{
Name = "Juri",
Address = new Address
{
Street = "Bolzano",
Country = "Italy"
}
};
(using inline initialization just for the purpose of this demo) and then cloning the object by calling
Person clone = p.Clone();
...you'll have the situation that the following statement will be true
clone.Address == p.Address;
If you read the documentation on MSDN it will also be clear why this is the case:
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. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.

One has to pay particular attention to this. If you ignore this and you expect to have different Address objects you may get weird behavior in your program which may be difficult to debug later on.

So basically what would be expected above is to not do a shallow copy, but a deep copy. In such a case you shouldn't expose the Clone() method yourself but rather implement the ICloneable interface. The deep copy logic has to be implemented by yourself. The example above could then look like this
public class Person : ICloneable
{
public string Name { get; set; }
public Address Address { get; set; }

#region ICloneable Members

public object Clone()
{
Person clone = MemberwiseClone() as Person;
if (this.Address != null)
{
this.Address = this.Address.Clone() as Address;
}

return clone;
}

#endregion
}

public class Address : ICloneable
{
public string Street { get; set; }
public string Country { get; set; }

#region ICloneable Members

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

#endregion
}
This could be one way of doing it. However be sure that you have the control of the whole object hierarchy. Cloning customer server controls (that inherit from the standard web controls) won't be such a good idea since there are too much dependencies among the object hierarchy which could lead to strange side effects. I was experiencing some of them, but I'll post an update as soon as I found the exact reason.

Brad Abrams mentions some comments on implementing the ICloneable interface which may be worth looking at.
Kindle

Comments

0

Your ad here?