A problem with implementing ICloneable
is that a storage location (field, variable, array slot, etc.) of a mutable reference type may be used to encapsulate identity, mutable state, both, or neither, but neither .NET nor any of its languages include any standard convention to indicate which of the above any given storage location is supposed to encapsulate.
If a field Foo1
encapsulates identity but not mutable state, then George.Clone().Foo1
should refer to the same instance as George.Foo1
.
If a field Foo2
encapsulates mutable state but not identity, then George.Clone().Foo2
should refer to a new object which is initialized to have the same state as George.Foo2
.
If a field Foo3
encapsulates neither mutable state nor identity [i.e. only immutable state other than identity], then George.Clone().Foo3
may refer to George.Foo3
, a new object with the same state, or any other convenient object with the same state.
If a field Foo4
encapsulates both mutable state and identity, instances of the type cannot be meaningfully cloned using a Clone
method.
If there were different storage location types for things that encapsulate identity, mutable state, both, or neither, there would be little for a deep- versus shallow distinction, since any reference-type field or other storage location encapsulated by a type should be cloned by recursively applying the above rules (note that for properly-constructed objects this cannot result in endless recursion, since two objects cannot meaningfully encapsulate each others' mutable state without at least one of them also encapsulating the other's identity). Unfortunately, because no such distinctions exist in the type system, there's no practical solution except to either implement ad-hoc cloning methods, or else implement a cloning method which uses attributes to decide what it should do, and uses hard-coded behavior for built-in .NET types which don't include any special indicators.