26.5 序列化
在介绍什么是序列化之前,先来看看序列化的一些主要应用场景:
❑在进程下次启动时读取上次保存的对象的信息。
❑在不同的应用程序域或进程之间传递数据。
❑在分布式应用程序中的各应用程序之间传输对象。
……
所谓序列化,就是将对象先转换为一种适当格式,然后再将其传输到目标位置的过程。所谓适当格式,有时候需要是二进制格式,有时候需要SOAP格式或者其他的XML格式,也可以是应用程序所特有的、定制化的格式。因此,可以将序列化视为将对象的状态保存到流或缓冲区的方法。和序列化相反的就是反序列化,就是把对象或数据从序列化的状态恢复为其原始状态的过程,如图26-8所示。
图 26-8 序列化和反序列化
.NET提供了三种预定义的格式化程序:
❑BinaryFormatter
❑SoapFormatter
❑XmlSerializer
接下来分别使用上述介绍过的每种方式对一个对象进行序列化和反序列化。这里要操作的类是Person,它的代码如代码清单26-8所示。
代码清单26-8 Person类[Serializable]
public class Person{
//私有字段
private string name=“张三”;
private ArrayList favourites=new ArrayList();private double weight;
//公共字段
public string height;
//公共字段,但加了"NonSerialized"属性[NonSerialized]
public string birthday;//默认构造函数
public Person(){}
//自定义构造函数,用于初始化只读集合属性Favourites public Person(ArrayList favourites)
{
this.favourites=favourites;}
//只读属性
public string Name{
get{return name;}}
//只读集合属性
public ArrayList Favourites{
get{return favourites;}}
//方法
public void SetWeight(double weight){
this.weight=weight;}
//方法
public override string ToString(){
StringBuilder builder=new StringBuilder();builder.Append("Name:"+Name+"\n");
builder.Append("Height:"+height+"\n");
builder.Append("Birthday:"+birthday+"\n");builder.Append("Weight:"+weight+"\n");
builder.Append("Favourites:");
for(int i=0;i<favourites.Count;i++){
builder.Append(favourites[i]+"\n");}
return builder.ToString();}
}
现在,大家已经看了这个类的代码。要说明的是,这是一个经过特别设计的类。在这里先埋个伏笔,或者说给读者留两个问题,答案将在后文中揭晓。这两个问题是:
❑Person类中包含了公共字段、私有字段、公共只读属性、公共只读集合属性以及公共方法。为什么这样设计呢?
❑Person类包含两个构造函数,一个是不带参数的默认构造函数,一个是带一个参数的自定义构造函数。这是为什么呢?能不能去掉默认构造函数?
以上两个问题的答案会在稍后揭晓,请大家注意思考。
26.5.1 BinaryFormatter
BinaryFormatter类允许使用二进制格式将对象序列化和反序列化,它位于System.Runtime.Serialization.Formatters.Binary命名空间。
使用这种方式,除了明确标注为不参与序列化的成员,一个对象中所有可以序列化的成员,包括当前对象引用的其他对象,都可以序列化。结果就是所有相关对象都会被复制一份新的拷贝,而不是简单地复制引用。在第18章介绍“数组的深复制”时用到了BinaryFormatter。
代码清单26-9 使用BinaryFormatter进行序列化和反序列化
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
namespace ProgrammingCSharp4
{
class SerializeSample
{
//序列化后生成的文件名
string strFile="person.dat";
public static void Main(string[]args)
{
ArrayList favourites=new ArrayList();
favourites.Add(“看电视”);
favourites.Add(“看电影”);
favourites.Add(“上网冲浪”);
Person person=new Person(favourites){height="175cm",birthday="2000-1-1"};
person.SetWeight(80.00);
Console.WriteLine(person.ToString());
SerializeSample ss=new SerializeSample();
ss.Serialize(person);
Person person2=ss.DeSerialize();
Console.WriteLine(person2.ToString());
}
//序列化
public void Serialize(Person person)
{
using(FileStream fs=new FileStream(strFile,FileMode.Create))
{
BinaryFormatter formatter=new BinaryFormatter();
formatter.Serialize(fs,person);
}
}
//反序列化
public Person DeSerialize()
{
Person person;
using(FileStream fs=new FileStream(strFile,FileMode.Open))
{
BinaryFormatter formatter=new BinaryFormatter();
person=(Person)formatter.Deserialize(fs);
}
return person;
}
}
}
上述代码的运行结果如下,注意观察,birthday字段使用了属性:[NonSerialized],表示不序列化次字段,其他的值无论是公共的还是私有的,也不管是否只读,全部都被序列化了。
Name:张三
Height:175cm
Birthday:2000-1-1
Weight:80
Favourites:看电视
看电影
上网冲浪
Name:张三
Height:175cm
Birthday:
Weight:80
Favourites:看电视
看电影
上网冲浪
请按任意键继续……
图26-9说明了这一过程。
图 26-9 使用二进制格式序列化