Supporting Clone with Anchor 4

Implementing a generic Clone method is surprisingly difficult for all but the simplest of classes. Even answering the question, “What should IClonable.Clone do?” proved so difficult that Microsoft deprecated the interface.

The first question is whether a clone should be shallow or deep. A deep clone is one where all of the child objects are also cloned, while a shallow clone reuses them. But there’s also a 3rd option. If a class is immutable, then objects of that class don’t need to be cloned. So a generic clone method could take that into consideration.

In .NET we use the Pure attribute to indicate a class behaves as if it were immutable. That’s not the same as being actually immutable. Performing on operations on a pure object should have no visible side-effects, but internally it may be doing things like caching values.

Currently our MetadataCache.Clone command only supports shallow and deep copies. We’re looking to include support for immutable/pure objects in Anchor 4.1.

In Anchor we have the concept of a property bag. A property bag is used instead of normal fields to back properties so that we offer things like change tracking and two-level undo. There is a performance cost to this, but the benefits outweigh it when you need to support IEditableObject and IRevertableChangeTracking in a WinForms or WPF application.

Inside the property bag is an array where the actual values are stored. Since this is just an array, we can cheaply copy it when performing a clone operation against a subclass of ModelBase or ModelCollection. This is done via the CloneOptions.BypassProperties flag.

Another flag that MetadataCache.Clone honors is CloneOptions.UseIClonable. When set, it will use IClonable.Clone instead of copying each property one by one.

But again, IClonable is deprecated. So perhaps an object exposes a Clone method without using that interface. To address that, we’re considering adding a new option in Anchor 4.1 to honor those methods.

The next wrinkle is that not all classes have a default constructor. When that happens, you have to match up parameters in the constructor with properties in the object being cloned. And of course, you may need to deep-clone those values before passing them to the constructor. So that’s another feature to consider for the future.

Speaking of deep clone, that’s just begging for an infinite loop. So to prevent problems, a cycle detection scheme should be used. Under this model, if an object is seen before it won’t be cloned a second time. This is fairly complex, so for now Anchor just uses a maximum recursion parameter.

Adding together MetadataCache.Clone and the planned work for the future, can we safely say that Anchor will truly have a generic clone function? No, because there are other edge cases such as non-public fields and events to consider.

Instead, Anchor is offering is a starting point for creating your own class-specific clone method. Unless your class is very simple, we expect you to expand upon it from time to time.

Leave a Reply

Your email address will not be published. Required fields are marked *