Why can't I type Clone() properly?

346 Views Asked by At

Like any other C# programmer, I face the problem of how best to express the copying of objects. Specifically, I have a class hierarchy in which all objects must be copyable. It has already been discussed whether to use copy constructors (which C# doesn't provide) or a Clone() method; another option is to use a method that takes an object and produces a copy. All options have pros and cons, but one issue common to all of them is the typing problem.

I would like to state, in whatever way possible, that there exists a copy operation on all objects in my class hierarchy that always produces an object of the same type as the original - in such a way that any violation against this is reported at compile time.

None of these three options allow me to do this, even with generics. Apparently, C# doesn't allow me to do it at all; neither do Java and some other languages I know.

Why not? Is such a feature in the type system too hard to implement? Does it cause inconsistencies? Is my wish for an accurately typed copy operation considered a corner case? Is there some other way of achieving this that I've missed?

PS: Please note that this question is not about the difference between shallow and deep copy, how to copy, or whether to do it at all.

4

There are 4 best solutions below

2
On

Part of the issue here is polymorphism. Creating a generic ICloneable<T> is trivial, but you get into problems when you have:

Foo : ICloneable<Foo>
Bar : Foo

because now Bar needs to implement both ICloneable<Foo> and ICloneable<Bar>. Fortunately, you can use method hiding and polymorphism together to make this just about work, i.e.

interface ICloneable<T> {
    T Clone();
}
class Foo : ICloneable<Foo> {
    protected virtual object Clone() { /*...*/ }
    public Foo Clone() { return (Foo) Clone(); }
}
class Bar : Foo, ICloneable<Bar> {
    protected override object Clone() { /*...*/ }
    public new Bar Clone() { return (Bar) Clone(); }
}

However! As you can see, this quickly gets painful to maintain. Perhaps a better option is to use the inbuilt non-generic ICloneable and an extension method:

public static T TypedClone<T>(this T source) where T : class, ICloneable
{
    if(source == null) return null;
    return (T)source.Clone();
}

and then you can just have Foo : ICloneable, and just use:

Foo foo = ...
Foo clone = foo.TypedClone();

with your Foo / Bar just:

class Foo : ICloneable {
    public virtual object Clone() { /*...*/ }
}
class Bar : Foo {
    public override object Clone() { /*...*/ }
}
3
On

If you want to implement some cloning logic in your class hierarchy, why don't you declare your own interface like this:

interface IMyCloneable<T>
{
    T Clone();
}

Implement it in every type, which supports cloning.

4
On

There is always ambiguity about the meaning of the word copy.

Do you mean a deep copy or a shallow copy?

Take a language like Java, which has only primitive types and references, and take a class such as:

class PairOfArrays {
   int[] i1;
   int[] i2;
}

Now, when you talk about copying an object of the above class, you probably want a deep copy, since you expect your arrays to be both duplicated (if you change an element of the array in the copied object, the source object won't be affected.

Take the following case, instead:

class PairOfStrings {
   String s1;
   String s1;
}

If you copy this object, would you require a deep copy still? String is an unmutable type in java, so in this case a shallow copy would be enough, and the copied PairOfStrings object would just refer to the same strings as the source object.

It could get worse. There would be cases where having a deep copy instead of a shallow copy will do damages.

class Wheel {
   Car parentCar;
}

In the above, parentCar is obviously a reference to the wheel's parent Car object, so all 4 wheels should point to the same parent.

You want to copy a wheel now, and you'd better make a shallow copy of you will duplicate the whole car.

As you can see, it is not so obvious with object-oriented programming to implement the copy procedure of an object.

This is why Java uses Clone() method to let you define what you mean by copy, since it cannot know it automatically, it is a design issue.

If you just want a deep copy of an object, though, you can always use Serialization. It works like a charm.

4
On

I was thinking again about this question, and personally came to the conclusion that such a feature would have little meaning for the following reasons:

Let's suppose you have an object hierarchy defined by yourself:

  • MyObject1
  • MyObject2
  • MyObject3

Your requirement would be that :

  • MyObject1 must have a MyObject1 clone() method
  • MyObject2 must have a MyObject2 clone() method
  • MyObject3 must have a MyObject3 clone() method

Now, suppose you use any of those methods directly, as in

MyObject2 copy = someMyObject2.clone();

Then, the compiler directly type-checks your method call, making your requirement useless.

Suppose you don't use it directly, then having the requirement is kind of useless, because you have a method you never use anyway.

Lastly, suppose you want to use a common interface that allows you to copy object of your hierarchy without knowing their type:

List<Copyable> list = ...
for( Copyable obj : list ){
   Copyable copy = obj.clone();
}

interface Copyable {
    public Copyable copy();
}

In this case too, stating that "Every object must declare a copy method with the return type being the same as the class of the object" is quite useless, since you won't be using the objects class directly anyway.

So question to your question, why would you want such a property? I think this is the line of thought that lead OOP languages not to support such a feature (I might be wrong).