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.
Questions? Thoughts? Hit me up
on Twitter