Wednesday, February 11, 2009

Write deep Clone(). Forget about ICloneable.

I was chatting with my co-worker about implementing Clone() method for our hierarchy of classes. It's not easy to implement interfaces (think about ICloneable) for class hierarchies. Some tips might be found in Implementing Interfaces at C# Online.NET article, in Implementing Interfaces: ICloneable and IComparable article, and in Advantages of ICloneable? discussion.
As Zach said, it's easier just to implement a virtual Clone() method in a base class and to override it in derived classes as necessary.

I started to look for information about ICloneable and found the following quite unequivocal guidelines:

Caution. Avoid implementing ICloneable. As alarming as that sounds, Microsoft is actually making this recommendation. The problem stems from the fact that the contract doesn’t specify whether the copy should be deep or shallow. In fact, as noted in Krzysztof Cwalina and Brad Abrams’ Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (Boston, MA: Addison-Wesley Professional, 2005), Cwalina searched the entire code base of the .NET Framework and couldn’t find any code that uses ICloneable. Had the Framework designers and developers been using this interface, they probably would have stumbled across the omission in the ICloneable specification and fixed it.
However, this recommendation is not to say that you shouldn’t implement a Clone method if you need one. If your class needs a clone method, you can still implement one on the public contract of the class without actually implementing ICloneable.

Accelerated C# 2008

Because the contract of ICloneable does not specify the type of clone implementation required to satisfy the contract, different classes have different implementations of the interface. Consumers cannot rely on ICloneable to let them know whether an object is deep-copied or not. Therefore, we recommend that ICloneable not be implemented.
The moral of the story is that you should never ship an interface if you don't have both implementations and consumers of the interface. In the case of ICloneable, we did not have consumers when we shipped it. I searched the Framework sources and could not find even one place where we take ICloneable as a parameter.
x DO NOT implement ICloneable.
x DO NOT use ICloneable in public APIs.
x CONSIDER defining the Clone method on types that need a cloning mechanism. Ensure that the documentation clearly states whether it is a deep- or shallow-copy.

Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries