Item 27 避免使用ICloneable接口
实现ICloneable接口,看起来是个不错的选择,想要类型支持拷贝,就实现ICloneable,不想支持拷贝,就不实现ICloneable。但是,大家仔细想一想,你的对象并不是在一个独立的环境中运行,需要考虑到对派生类的影响,基类已经实现了ICloneable接口,派生类也继承了基类的Clone方法,所以派生类最好也支持ICloneable接口,这样所有派生类都应保持一致,所以所有派生类都应实现ICloneable接口,还需要考虑到类的成员都必须支持ICloneable接口或提供一种机制支持拷贝,如果支持深拷贝的对象包含有网状结构的对象,就会使拷贝很成问题。
这里谈到了深拷贝,对应的是浅拷贝。所谓浅拷贝,是创建一个新对象,然后将原对象的所有成员拷贝到新对象中,如果某成员是引用类型,那么仅仅拷贝引用给新对象,也就是说新,旧对象的那个成员引用的是同一个对象。而深拷贝是拷贝所有成员并对引用类型的对象进行递归的拷贝,即对引用的对象也进行拷贝(而不仅仅是引用的拷贝,并且是递归下去,直到递归到只有值类型成员的拷贝)。像值类型和String类型,深拷贝和浅拷贝效果一样,都是完全复制,得到一个与原对象无关新对象(只有内容一样,没有其它关联,对两者中任何一个的修改,不会影响到另一个对象)。
那么当我们定义的类型实现了ICloneable接口时,我们应该实现深拷贝还是浅拷贝呢,这取决于类型本身,但同时在一个类型中混用深拷贝和浅拷贝会导致很多不一致问题,一旦实现了ICloneable接口,这种混用就很难避免,所以定义类型时应该尽量避免实现ICloneable接口,让类更简单一些,使用和实现都相对简单一些。
任何只以值类型和String类型对象作为成员的值类型都不用支持ICloneable接口,用简单的赋值语句要比Clone方法高效得多,Clone方法要对返回的对象进行装箱,才能强制返回一个System.Object对象,而调用者使用对象时又要对其进行拆箱,这又是何苦呢?例如:
{
private int _errorCode;
private int _details;
private string _msg;
}
{
private string _label = "PeterLau";
private int[] _values = new int[10];
public object Clone()
{
BaseType baseType = new BaseType();
baseType._label = this._label;
baseType._values = this._values;
return baseType;
}
}
public class DerivedType : BaseType
{
private double[] _dValues=new double[10];
static void Main()
{
DerivedType derivedType1 = new DerivedType();
DerivedType derivedType2 = derivedType1.Clone() as DerivedType;
if (derivedType2 == null)
Console.WriteLine("null");
}
}
{
private string _label = "PeterLau";
private int[] _values = new int[10];
public BaseType()
{ }
protected BaseType(BaseType baseType)
{
_label = baseType._label;
_values = baseType._values.Clone() as int[];
}
}
public class DerivedType : BaseType,ICloneable
{
private double[] _dValues=new double[10];
public DerivedType()
{
}
private DerivedType(DerivedType right)
: base(right)
{
_dValues = right._dValues;
}
public object Clone()
{
DerivedType derivedType = new DerivedType(this);
return derivedType;
}
static void Main()
{
DerivedType derivedType1 = new DerivedType();
DerivedType derivedType2 = derivedType1.Clone() as DerivedType;
if (derivedType2 == null)
Console.WriteLine("null");
}
}
基类并不实现ICloneable接口; 通过提供一个受保护的构造函数,让派生类可以拷贝基类的成员。叶子类,应该都是密封的,必要它应该实现ICloneable接口。基类不应该强迫所有的派生类都要实现ICloneable接口,但你应该提供一些必要的方法,以便那些希望实现ICloneable接口的派生类可以使用。
ICloneable接口有它的用武之地,但相对于它的规则来说,我们应该避免它。对于值类型,你不应该实现ICloneable接口,应该使用赋值语句。对于引用类型来说,只有在拷贝确实有必要存在时,才在叶子类上实现对ICloneable的支持。基类在可能要对ICloneable 进行支持时,应该创建一个受保护的构造函数。总而言之,我们应该尽量避免使用ICloneable接口。

浙公网安备 33010602011771号