Copy Constructors vs ICloneable -- Redux (Updated!)

Back in 2002, I wrote an article for ONDotNet.com, about .NET's copying, cloning, and marshalling semantics.  I suggested that the role of C++ style copy constructs was diminished, in the face of ICloneable -- in a section obliquely entitled "The Diminished Role of Copy Constructors in C#".  ;-)

It's taken me a while to fully realize it, but boy, was I wrong!

The trouble with ICloneable, as it stands, is that it's very difficult for derived classes to override its implementation.  Consider the following naive code, a class derived from an ICloneable base class:

class Base : ICloneable
{
  private int baseState;

  object ICloneable.Clone()
  {
    Base clone = new Base();
    clone.baseState = this.baseState;
    return clone;
  }
}

class Derived : Base
{
  private float derivedState;
  
  //bug: clients of ICloneable will lose our derivedState
  // (not to mention our entire type-identity).
}

When clients of this derived type go to create a clone, it's easy to see what'll go wrong -- a new object of the base type is being returned -- the derived type has been completely dropped.  Yikes!

What to do?  Derived classes have access to their base type's protected members, but not to private or (necessarily) to internal members.  So the derived type can't simply reproduce the base class's cloning logic (not that that's a good idea), nor even call up to the base class's Clone implementation (a separate instance of the base type would be returned -- that's no help).

Even if the base class implements ICloneable.Clone as a public, overridable method (instead of as a private, explicit interface implementation, as I've done above) the derived class is no better off -- it still doesn't have a way to ask the base class to copy its state into a new instance of the derived-type.  The following code illustrates the crux of the problem:

class Base : ICloneable
{
  private int baseState;

  object ICloneable.Clone()
  {
    Base clone = new Base();
    clone.baseState = this.baseState;
    return clone;
  }
}

class Derived : Base, ICloneable
{
  private float derivedState;
    
  object ICloneable.Clone()
  {
    Derived clone = new Derived();
    clone.derivedState = this.derivedState;
    return clone; //bug: we've lost our baseState!
  }
}

Next idea.  One could take an approach similar to that of many IDisposable implementations -- implement the interface, on the base class only, by calling down into a different virtual method.  The virtual method invocation should allow the most-derived type to perform the actual instantiation of the clone -- after all, it's the only class that knows what type to instantiate -- and then call up the inheritance chain to allow each base class a chance to copy its state into the new clone.  Any way you cut it, it's a complex, fragile design with a lot of messy down-casting.  Such a design might look something like this:

class Base : ICloneable
{
  private int baseState;

  object ICloneable.Clone()
  {
    // Allow most-derived type to instantiate the 
    // new clone, and all other derived types to 
    // copy state into it.
    return CloneImpl(null);
  }
  
  protected virtual object CloneImpl(object clone)
  {
    // Instantiate the clone, if a derived type 
    // hasn't already.
    if (clone == null) clone = new Base();

    // Copy our state into the clone.
    ((Base)clone).baseState = this.baseState;
    return clone;
  }
}

class Derived : Base
{
  private float derivedState;

  protected override object CloneImpl(object clone)
  {
    // Instantiate the clone, if a derived type hasn't 
    // already.
    if (clone == null) clone = new Derived();
      
    // Allow base type(s) to copy their state into the 
    // new clone.
    base.CloneImpl(clone);

    // Copy our state into the clone.
    ((Derived)clone).derivedState = this.derivedState;
    return clone;
  }
}

No, a much more reasonable approach to this design problem is the good 'ol copy contructor!  If every class in the inheritance chain, up to the highest-level implementer of ICloneable, provides a copy constructor (a constructor, with protected or greater accessibility, which takes an instance of its own type as the sole argument) the problem is solved.  Derived types can (re)implement ICloneable, or override the base class's Clone method, and simply create a rich copy of themselves using their own copy constructor -- which, in turn, calls up the chain of base classes' copy constructors.  Voila!  A full-fledged clone of the entire class, base types and all.  And with reasonably clean and simple code, to boot:

class Base : ICloneable
{
  private int baseState;

  protected Base(Base that)
  {
    this.baseState = that.baseState;
  }
  
  object ICloneable.Clone()
  {
    // Delegate to copy ctor.
    return new Base(this);
  }
}

class Derived : Base, ICloneable
{
  private float derivedState;

  protected Derived(Derived that) : base(that)
  {
    this.derivedState = that.derivedState;
  }
  
  object ICloneable.Clone()
  {
    // Delegate to copy ctor.
    return new Derived(this);
  }
}

There is another approach worth mentioning -- using MemberwiseClone() in the base class's implementation of ICloneable, instead of new Base(), will initialize the clone as a memberwise-copy of the most-derived type.  This is what we want.  The code looks like this:

class Base : ICloneable
{
  private int baseState;

  public virtual object Clone()
  {
    // Use MemberwiseClone to get a placeholder clone, of 
    // the proper most-derived type.
    Base clone = (Base)this.MemberwiseClone();
    
    // Perform any additional deep-copying needed, then return.
    //...

    return clone;
  }
}

class Derived : Base, ICloneable
{
  private float derivedState;

  public override object Clone()
  {
    // Call base class impl.
    Derived clone = (Derived)base.Clone();

    // Perform any additional deep-copying needed, then return.
    //...

    return clone;
  }
}

It's a very good approach, the only marks against it are very minor.  First, you have to remember to actually do it.  It's way too easy to "new" yourself, instead of copying yourself via MemberwiseClone.  Second, you must expose the Clone method as a public, virtual method to be overridden by derived classes -- you can't use C#'s explicit interface implementation syntax.  Why?  The derived type won't be able to call up to your implementation.  Why care?  With an explicit (hidden!) implementation of ICloneable, we're free to provide a strongly-typed public method, to go along with it:

class Base : ICloneable
{
  private int baseState;

  public virtual object Clone()
  {
    // Use MemberwiseClone to get a placeholder clone, of 
    // the proper most-derived type.
    Base clone = (Base)this.MemberwiseClone();
    
    // Perform any additional deep-copying neededm then return.
    //...

    return clone;
  }

  public virtual Base Clone() // won't compile: CS0111!
  {
    return (Base)Clone();
  }
}

class Derived : Base, ICloneable
{
  private float derivedState;

  public override object Clone()
  {
    // Call base class impl.
    Derived clone = (Derived)base.Clone();

    // Perform any additional deep-copying neededm then return.
    //...

    return clone;
  }

  public override Derived Clone() // won't compile: CS0111!
  {
    return (Derived)Clone();
  }
}

There are a few more approaches I've seen -- like, using Activator.CreateInstance(this.GetType()) to instantiate the most-derived type from within the base class's implementation.  (This is the approach that WinForms's ListViewItem class uses, for example.)  In addition to being more expensive than MemberwiseClone (likely, I haven't checked) it has the generally undesirable side effect of requiring an accessible default constructor on the derived type.  That's probably reasonable for ListViewItem-derived types, but it's not for everyone.

Happy cloning!

Updated: Thu, 16 Sep 2004 06:58:39 GMT