ICloneable: Why you should never use it.

Ask just about any developer and they will agree that cloning objects is a pretty standard practice. C++ has its copy constructors, …, and C# has it’s…ICloneable interface?

Okay, seriously, what is the deal with this thing? Is it supposed to perform a shallow copy, or a deep copy? Why doesn’t it output a cloned object that is strongly typed? Why is a half-completed interface a part of the .NET framework? Who allowed for this to be merged into the master branch? Does that person still have a job? Jokes aside, I am not the only one asking these questions. Do a simple Google search for the term “ICloneable” and you’ll find hundreds of thousands of questions all asking the same thing.

Microsoft has gone as far as to publicly announce that developers should not use the ICloneable interface, and for good reason. Apparently, the interface was introduced with the idea that developers would decide the behavior of it in production. That is, Microsoft assumed that developers would adopt the interface and set strict project guidelines that enforced whether or not the interface would perform deep or shallow copies. Or at least that is their intention going forward.

The fact that ICloneable doesn’t return a strongly-typed copy is worrisome; even more so because it does not have a generic version implemented in the framework. That is, you cannot use, say, ICloneable and receive a copy of type CopyMe. Rather, it returns a boxed object type, which then requires you to cast it back to the intended type. This is really mind-boggling. It makes absolutely zero sense.

The bottom line is, developers should never use the ICloneable interface. Rather, they should use tried-and-tested patterns that allow for much cleaner code and better performance.

 

How it should be implemented:

The tried-and-tested copy constructor is one of the most preferred method for creating an object clone. Whereas ICloneable is vague in its intended use, copy constructors force developers to explicitly define copy logic. That is, if extra steps need to be taken in order to ensure a deep clone of an object, such as copying child objects, it is well-defined. This leaves very little room for undefined behavior.

C# has multiple ways of implementing this, but my personal favorite is by using an initializer. If you simply need to set a few properties, you can easily manage this with the following implementation:

class CopyMe {
  public string Name {get; private set;}
  public int Age {get; private set;}
  public CopyMe Child {get; private set;}

  CopyMe(string name, int age, CopyMe child = null) {
    Name = name;
    Age = age;
    Child = child;
  }

  CopyMe(CopyMe other) {
    Name = other.Name;
    Age = other.Age;
    
    if(other.Child != null)
      Child = other.Child;
  }

  CopyMe Copy() {
    return new CopyMe {
      Name = Name,
      Age = Age,
      Child = Child == null ? null : Child.Copy()
    };
  }
}

These constructors all perform a deep copy of the CopyMe class, including any nested children associated with that object. It is worth noting that the initialier used in the Copy() function is slightly more performant than calling the actual copy constructor, as it requires one less call and uses variable localization to set the values. The DotNet JIT compiler will likely optimize this as it is called, however, so this is often negligible.

 

The main takeaway from this is that a developer should never use the ICloneable interface. It is incomplete, poorly defined and documented, and essentially deprecated by its own author. If object copying is needed, it is better to write a custom interface or logic that allows you to explicitly define and control the copy behavior.

Leave a Reply