利用对象序列化深表克隆对象

博客围绕.NET对象克隆展开。在.NET中,变量赋值常指向同一对象,可通过实现ICloneable接口克隆对象,但MemberwiseClone方法有局限,只能浅表复制且需改源代码。使用对象序列化可解决这些问题,还给出了通用的深表克隆方法及使用示例。

在.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;
}
}

以下,是一个使用这个方法克隆的例子:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;

namespace ObjectCloneBySerialize
{

class Class1
{

[STAThread]
static void Main(string[] args)
{
ArrayList Persons = new ArrayList();
Person Fangxing = new Person("Fang Xing", "Male");
Persons.Add(Fangxing);
Person Liwei = new Person("Li Wei", "Male");
Persons.Add(Liwei);

ArrayList ClonedPersons = (ArrayList)CloneObject(Persons);
Person Zhangsan = new Person("Zhang San", "Male");
ClonedPersons.Add(Zhangsan);

Console.WriteLine("===== Before Clone =====");
for(int i=0; i<Persons.Count; i++)
{
Person someone = (Person)Persons[i];
Console.WriteLine(someone.Name + " " + someone.Gender);
}

Console.WriteLine("===== After Clone =====");
for(int i=0; i<ClonedPersons.Count; i++)
{
Person someone = (Person)ClonedPersons[i];
Console.WriteLine(someone.Name + " " + someone.Gender);
}

Console.ReadLine();

}

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;
}
}
}

[Serializable]
class Person
{
private string m_Name;
private string m_Gender;

public string Name
{
get{return m_Name;}
}

public string Gender
{
get{return m_Gender;}
}

public Person(string name, string gender)
{
m_Name = name;
m_Gender = gender;
}

}
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值