How to clone an inherited object?
I've got a Tile
class with this method:
public object Clone()
{
return MemberwiseClone();
}
And another class Checker
that inherits from Tile
.
I also have a Board
class that is a List<Tile>
. I want to clone the board, so I wrote this:
public Board Clone()
{
var b = new Board(width, height);
foreach (var t in this) b.Add(t.Clone());
return b;
}
But it throws an error:
cannot convert from 'object' to 'Checkers.Tile'
Now I can make the Tile.Clone
method return a Tile
instead, but then will the MemberwiseClone
copy the additional properties in the sub-Checker
as well?
If that's not the problem, what's the semantic difference between the above Board.Clone
method and this?
public Board Clone()
{
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, this);
ms.Position = 0;
return (Board)bf.Deserialize(ms);
}
}
Because they're definitely having different effects on my program, even though when I print the board it looks the same. I don't think something is being cloned but a reference is being returned. The Board
ctor looks like this:
public Board(int width = 8, int height = 8)
{
this.width = width;
this.height = height;
this.rowWidth = w开发者_如何学Pythonidth / 2;
this.Capacity = rowWidth * height;
}
The Tile
class actually doesn't have any properties. The checker just has two enums properties:
public enum Color { Black, White };
public enum Class { Man, King };
public class Checker : Tile
{
public Color Color { get; set; }
public Class Class { get; set; }
Yes, MemberwiseClone will also copy the Checker
-only fields. MemberwiseClone cannot know the return type of your Clone
method; therefore, it's behaviour cannot depend on it.
About the difference betweeen your Clone implementation and the serialization: MemberwiseClone
creates a shallow copy of the Tiles: If a Tile (or Checker) references some object, the Tile's clone still references the same object (rather than a copy of it).
On the other hand, your serialization code is a well known practice for creating a deep copy of your board: The whole tree of dependent objects is serialized and deserialized.
Of course, this only makes a difference if your Tiles (or Checkers) contain fields with reference types.
As I see it, there are four main classes of object, with respect to cloning:
- Those which cannot be cloned without breaking something
- Those which make no public promise of cloneability, but can be nicely cloned using MemberwiseClone
- Those which make no public promise of cloneability, but can be cloned via some means other than MemberwiseClone
- Those which publicly advertise cloneability on behalf of themselves and derived classes.
A semi-cloneable object should support a protected virtual method called something like CloneBase which will return either Object or the base class type (it won't matter much in practice); the lowest-level CloneBase method should call MemberwiseClone and do whatever is necessary to fix up the cloned object. Any derived class which supports cloning should have a public Clone method which simply calls CloneBase and typecasts the result. Any derived-class logic necessary to fix up and object after base-class-level cloning should go in an override of CloneBase.
If there may be any need for a derived class which does not support cloning, then make public a semi-cloneable class and inherit from that class a CloneableWhatever which does nothing except add the public Clone method. In that way, non-cloneable classes can derive from the semi-cloneable class, and cloneable ones can derive from CloneableWhatever.
Yes, it will - it's polymorphism.
精彩评论