views:

489

answers:

5

In the past I had the need to clone objects, only to find that they don't implement a Clone() method, forcing me to do it by hand (create a new instance and copy all properties from the original to the new one)

Why isn't cloning as easy as duplicating the memory block the object is allocated in, and thus have the Clone method in the object class, having all classes in .NET inherit it?

+20  A: 

Because that wouldn't perform a deep clone, which is usually what clones really need to be. Imagine you have a reference to an array, or a list... simply copying the memory taken by your object will simply clone the reference. Any changes to the array will be visible through the clone as well as the original object - so the two objects are still connected, which violated the normal point of cloning.

If you want to implement exactly that functionality, it's easy - that's what Object.MemberwiseClone() is for. Most of the time, if it even makes sense to clone an object (what does a cloned NetworkStream mean?) it makes sense to clone each property... unless it refers to an immutable value already, etc. In other words, this is a naturally hard problem, which is why most types don't support cloning.

If you stick to immutable types wherever possible, it's not so much of an issue... that makes other things harder, admittedly, but it can be very powerful in many cases.

Jon Skeet
Immutable types are full of win. Also, note that MemberwiseClone is protected or else you could seriously break semantics down to core ECMA-335 specifications like the fact that two System.Type objects are equal iff they reference the same object in memory.
280Z28
At the same time I wrote something similar in indifferent words. I think it is a pity that .NET defines "by-ref" and "by-val" on the type (class/struct) and not the field/variable holding it. This is actually the information you need for cloning... and other things.
Stefan Steinegger
@Stefan: That topic is the subject of my research project this semester. It turns out it doesn't cause a fundamental problem with language expressiveness, but the reasoning behind it is not what I originally thought.
280Z28
Wouldn't it be conceivable to have a method `DeepClone` which deeply clones assuming all objects implement the `DeeplyCloneable` interface (idea in rough sketches)? Obviously this would affect many many classes and perhaps cloning is not that popular an activity.
Yar
@yar: Yes, there could be a DeepClone method. There just isn't :)
Jon Skeet
@Jon Skeet, awesome, I'll send an email to the .Net team straight away
Yar
+2  A: 

There is such a thing as Object.MemberwiseClone(), which does what you describe. It is making a shallow copy of the object.

It won't do a deep clone automatically... you'll need to call clone on all the member objects manually, etc.

Jeff Meatball Yang
The only problem with it is that it's a protected method, so unless the class designer decided to expose it, you're out of luck.
Pavel Minaev
A: 

You have to explicitly implement the ICloneable interface in your classes.

However, as the documentation states, the cloning mechanism in MemberwiseClone does not distinguish between shallow and deep copy.

devio
+3  A: 

Others have already explained about MemberwiseClone, but no-one gave the explanation of why it is protected. I'll try to give the rationale.

The problem here is that MemberwiseClone just blindly copies the state. In many cases, this is undesirable. For example, the object might have a private field which is a reference to a List. A shallow copy, such as what MemberwiseClone does, would result in new object pointing to the same list - and the class may well be written not expecting the list to be shared with anyone else.

Or an object can have some sort of ID field, generated in constructor - again, when you clone that, you get two objects with the same ID, which may lead to all kinds of weird failures in methods assuming that ID is unique.

Or say you have an object that opens a socket or a file stream, and stores a reference to that. MemberwiseClone will just copy the reference - and you can imagine that two objects trying to interleave calls to the same stream isn't going to end well.

In short, "cloning" is not a well-defined operation for arbitrary objects. The fact that memberwise operator= is provided for all classes by default in C++ is more of a nuisance, as all too often people forget that it's there, and do not disable it for classes for which copying doesn't make sense, or is dangerous (and there are surprisingly many such classes).

Pavel Minaev
@Pavel: I hinted at the problem in my comment to Jon before this answer. Your answer is of course the much more complete explanation. :)
280Z28
+2  A: 

There are (at least) two kinds of cloning. Most references talk about shallow and deep cloning, but in reality there are shades inbetween.

The key problem is the tension between "how much should be copied" and "how much should be shared".

Consider an Order object, containing references to a Customer, an Address and to a List of `OrderLines'.

If you wanted to Clone() an Order, what's involved?

"Just copying the memory block" would give you a new Order, but one that shared the Customer, Address and the same List of `OrderLines'. (Remember that object members are stored by reference, so when you duplicate the memory block, you end up with two references to the same object).

Clearly, you don't want to share the List of OrderLines' between the two Orders. In fact, you probably want to clone each OrderLine` as well.

If you were working with a general purpose Clone() method, how would that method know which members should be recursively cloned, and which should not?

Generally speaking, this is an intractable problem - which is why it's up to individual objects to implement appropriate semantics for their situation.

One final note: even when I do create the ability to Clone() objects, I don't tend to create a Clone() method, instead prefering a copy constructor - a constructor that accepts another object as a basis. If you can't find Clone(), look for that.

Bevan