利用对象序列化深表克隆对象
在.NET上,一切都是对象。这种安排的后果之一就是在当将一个变量赋值给另一个变量的时候,会得到两个指向同一对象的变量,而不是两个不同的数据副本(除非是使用值类型而不是引用类型)。一般情况下,可以通过调用类所公开的一个特殊方法得到一个数据的副本。在.NET世界中,类应当实现ICloneable接口并公开这个接口的惟一方法Clone,让外部调用知道它能够创建类的实例的副本。框架中有多个对象可以实现这个接口,包括Array、ArrayList、BitArray、Font、Icon、Queue和Stack。
大多数的时候,实现ICloneable接口都相当的简单。所有其他类都是从System.Object 类继承来的。该类所定义的MemberwiseClone方法可以帮助复制对象,而无需手动复制对象的各个属性:
// Implements ICloneable.Clone 方法。
public object Clone()
{
// 对象属性如果是复杂类型,那么创建的仍然是其浅表副本。
// 对象属性如果是复杂类型,那么复制仍然需要手动。
// 只是利用MemberwiseClone复制所有的非对象值。
return this.MemberwiseClone();
}
但是,这种方法使我们使用时感到不爽,因为,只有通过更改对象定义的源代码才可以克隆一个对象,因为MemberwiseClone方法是受保护的,它只有在类的内部才可以被访问。还有,也是在多数情况下更为重要的一个方面,MemberwiseClone方法执行的是对象的浅表复制——也就是说,它创建了对象的一个副本,但是没有创建该对象所引用的其他任何对象的副本。(就是说,对象内部的数据成员如果是引用的话,还是得我们手动复制的,如果对象图非常复杂,那么我们所面对的工作是多么的繁杂,嗷嗷不爽。)
使用对象序列化来处理复杂的对象图可以同时解决前面所提到的两个问题。实际上,可以创建一个通用的方法来对任何对象进行深表克隆。
这是一个简单的提供序列化克隆对象方法:
public static object CloneObject(object obj)
{
// 创建内存流
using(System.IO.MemoryStream ms = new MemoryStream(1000))
{
object CloneObject;
// 创建序列化器(有的书称为串行器)
// 创建一个新的序列化器对象总是比较慢。
BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
// 将对象序列化至流
bf.Serialize(ms, obj);
// 将流指针指向第一个字符
ms.Seek(0, SeekOrigin.Begin);
// 反序列化至另一个对象(即创建了一个原对象的深表副本)
CloneObject = bf.Deserialize(ms);
// 关闭流
ms.Close();
return CloneObject;
}
}