It is unclear to me from the MSDN documentation if I should provide a deep or a shallow clone when implementing ICloneable. What is the preferred option?
Given the way an object is defined, there shouldn't be any question about "deep cloning" versus "shallow cloning". If an object encapsulates the identities of things, a clone of the object should encapsulate the identities of the same things. If an object encapsulates the values of mutable objects, a copy should encapsulate detached mutable objects holding the same values.
Unfortunately, neither .NET or Java includes in the type system whether references are held to encapsulate identity, mutable value, both, or neither. Instead, they just use a single reference type and figure that code which owns the only copy of a reference, or owns the only reference to a container which holds the only copy of that reference, may use that reference to encapsulate either value or state. Such thinking might be tolerable for individual objects, but poses real problems when it comes to things like copying and equality testing operations.
If a class has a field Foo
which encapsulates the state of a List<Bar>
which is to encapsulate the identities of objects therein, and may in future encapsulate different objects' identities, then a clone of the Foo
should hold a reference to a new list which identifies the same objects. If the List<Bar>
is used to encapsulate the mutable states of the objects, then a clone should have a reference to a new list which identifies new objects that have the same state.
If objects included separate "equivalent" and "equals" methods, with hashcodes for each, and if for each heap object type there were reference types that were denoted as encapsulating identity, mutable state, both, or neither, then 99% of equality testing and cloning methods could be handled automatically. Two aggregates are equal if all components which encapsulate identity or mutable state are equivalent (not merely equal) and those that encapsulate neither are at least equal; two aggregates are equivalent only if all corresponding components are and always will be equivalent [this often implies reference equality, but not always]. Copying an aggregation requires making a detached copy of each constituent that encapsulates mutable state, copying the reference to each constituent that encapsulates identity, and doing either of the above for those which encapsulates neither; an aggregation with a constituent which encapsulates both mutable state and identity cannot be cloned simply.
There are a few tricky cases that such rules for cloning, equality, and equivalence wouldn't handle properly, but if there were a convention to distinguish a List<IdentityOfFoo>
from a List<MutableStateOfFoo>
, and to support both "equivalent" and "equals" tests, 99% of objects could have Clone, Equals, Equivalent, EqualityHash, and EquivalenceHash auto-generated and work correctly.
Clones are deep by default, thats the naming convention and copy constructors can be shallow if they want, for performance reasons.
Edit: This naming convention goes beyond boundaries, its the same for .Net, Java, C++, Javascript, etc... the actual source is beyond my knowledge but its part of the standard Object Oriented lexicon, just like objects, and classes. Thus MSDN doesn't specify implementation because its a given by the word itself (of course lots of newcomers to OO languages don't know this, and they SHOULD specify it, but then again their documentation is quite frugal anyways)
Short answer: Yes.
Long Answer: Don't use ICloneable. That is because .Clone isn't defined as being a shallow or a deep clone. You should implement your own IClone interface, and describe how the clone should work.