解析
对象序列化可以使对象的数据持久地保存,对象状态可以是Ssytem.IO.Stream的派生类型。并不是任何类型的对象都可以被序列化的,只有当其类型定义为可序列化时,该对象才可以被序列化。例如,自定义一个类类型,当该类类型被标记了[Serializable]特性后,其对象即可被序列化。序列化的代码本身很简单,并可使用多种格式保存对象(或对象图),如二进制格式、SOAP格式和XML格式。
将对象以二进制格式序列化,需要使用BinaryFormatter类型,该类型位于mscorlib.dll程序集,使用时只需导入其命名空间即可,程序代码如以下代码所示:
//导入BinaryFormatter类的命名空间 using System.Runtime.Serialization.Formatters.Binary; //创建BinaryFormatter类对象MyBF BinaryFormatter MyBF = new BinaryFormatter(); //创建自定义类MyClass的对象引用obj,该类已经标记了[Serializable]特性 MyClass obj = new MyClass(); //创建Stream类型引用fs Stream fs = new FileStream(需要保存的路径, FileMode.Create, FileAccess.Write, FileShare.None); //调用MyBF的Serialize方法,传递fs和obj参数 MyBF.Serialize(fs, Pn); //关闭fs对象 fs.Close(); |
可见,只需调用BinaryFormatter类对象的Serialize()方法,即可完成序列化的操作。如果使用SOAP格式序列化,其操作和二进制格式基本一致,只是由SoapFormatter类的对象调用Serialize()方法。由于定义SoapFormatter类的命名空间是System.Runtime. Serialization.Formatters.Soap,而这个命名空间定义于System.Runtime.Serialization. Formatters.Soap.dll程序集内,所以使用SoapFormatter类时,需手动引用该程序集,然后导入该命名空间。实际上,BinaryFormatter类和SoapFormatter类都实现了IFormatter接口,所以在程序应用中,可以编写相应的多态程序。
XML格式序列化对象需要创建System.Xml.Serialization.XmlSerializer类对象,然后调用Serialize()方法。该类的命名空间虽然定义于System.Xml.dll程序集内,但是Visual Studio 2005/ Visual Studio 2008将自动引用该程序集,所以不需要手动引用System.Xml.dll程序集。XML格式序列化的方法如以下代码所示:
//导入XmlSerializer类的命名空间 using System.Xml.Serialization; //创建XmlSerializer类对象MyXS XmlSerializer MyXS = new XmlSerializer(typeof(MyClass), 要序列化其他对象的Type类型数组); //创建自定义类MyClass的对象引用obj,该类已经标记了[Serializable]特性 MyClass obj = new MyClass(); //创建Stream类型引用fs Stream fs = new FileStream(需要保存的路径, FileMode.Create, FileAccess.Write, FileShare.None); //调用MyXS的Serialize方法,传递fs和obj参数 MyXS.Serialize(fs, Pn); //关闭fs对象 fs.Close(); |
XmlSerializer类的构造函数需要传递参数,当只需要实现一个独立对象时,传递该对象所属类的Type对象类型即可,当该对象依赖于其他对象时,需要将其他对象所属类的Type对象类型作为数组传递。
说明:对象图是指一组关联的对象关系图,描述了多个对象之间的依赖关系。
面试例题21:如何将XML格式持久化的对象反序列化?
考点:反序列化的基本方法,处理反序列化后的对象数据。
出现频率:★★
解答
在Visual Studio 2005/ Visual Studio 2008中创建一个C#的Windows窗体应用程序项目,并将其项目命名为ListSerialize。程序序列化的对象是List<T>类的对象(该类是可序列化的),通过"添加"按钮控件,增加List<T>类对象中的子项,直到单击"输出XML"按钮。而单击"XML反序列化"按钮后,指定XML文件中的对象信息还原,并在ListBox控件(命名为"XmlList")中输出该对象的每个子项数据。在Visual Studio 2005/ Visual Studio 2008的"Form1.cs[设计]"视图中创建基本的窗体布局和控件,控件的命名如图7.50所示。
|
(点击查看大图)图7.50 反序列化程窗体控件布局及命名 |
编写ListSerialize项目的Form1.cs如代码7.26所示。
代码7.26 反序列化对象:Form1.cs
using System; ……………………………………… //导入必要的命名空间 using System.Xml.Serialization; using System.Runtime.Serialization; using System.IO;
namespace ListSerialize { public partial class Form1 : Form { //创建List<T>类型对象list,子项类型为PersonDetail List<PersonDetail> list = new List<PersonDetail>(); //声明List<T>类型readlist变量,子项类型为PersonDetail List<PersonDetail> readlist; //声明fn变量,用于存储文件路径 string fn; public Form1() { InitializeComponent(); }
private void AddBtn_Click(object sender, EventArgs e) { //创建PersonDetail类型对象Pd PersonDetail Pd = new PersonDetail(); //将两个文本框控件内容赋值给Pd的两个对象 Pd.Name = nametxt.Text; Pd.Password = passwordtxt.Text; //list对象添加子项Pd list.Add(Pd); MessageBox.Show("添加成功"); }
private void OutputBtn_Click(object sender, EventArgs e) { //获取用户输入的文件路径 fn = FileName.Text; //创建XmlSerializer的对象引用Xs,并传递List<PersonDetail>的类型 XmlSerializer Xs = new XmlSerializer(typeof(List<PersonDetail>)); //创建Stream类型引用fs,并传递fn作路径参数 Stream fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.None); //调用Xs的Serialize方法,传递fs和list参数 Xs.Serialize(fs, list); //关闭fs对象 fs.Close(); MessageBox.Show("XML序列化成功"); //将文本框控件清空 nametxt.Text = ""; passwordtxt.Text = ""; FileName.Text = ""; }
private void InputBtn_Click(object sender, EventArgs e) { string str; //获取用户输入的文件路径 fn = ReadFileName.Text; try { //创建FileStream类型引用fs,并传递fn作路径参数,文件模式为打开文件 FileStream fs = new FileStream(fn, FileMode.Open); //创建XmlSerializer类型对象Xs,传递List<PersonDetail>的类型 XmlSerializer Xs = new XmlSerializer(typeof(List<PersonDetail>)); //调用Xs的Deserialize方法,传递fs //将返回的对象转换为List<PersonDetail>类型,并将引用赋值给readlist readlist = (List<PersonDetail>)Xs.Deserialize(fs); //遍历readlist的每个子项 foreach (PersonDetail Pd in readlist) { //将子项的属性值组合后赋值给str变量 str = String.Format("姓名:【{0}】,密码:【{1}】", Pd.Name, Pd.Password); //调用XmlList控件Items的Add方法,将str添加为项 XmlList.Items.Add(str); } } //处理文件未找到异常 catch (FileNotFoundException ex) { string msg = String.Format("异常信息:{0}", ex.Message); MessageBox.Show(msg); } //将文件路径文本框控件清空 ReadFileName.Text = ""; } }
public class PersonDetail { //定义两个私有字段 string _name; string _password; //定义两个公共属性,可以读写相应的私有字段 public string Name { get { return _name; } set { _name = value; } } public string Password { get { return _password; } set { _password = value; } } public PersonDetail() { } } } |
程序运行后,在前两个"TextBox"控件中输入一组值并单击"添加"按钮,结果如图7.51所示。
用户每单击一次"添加"按钮,list对象中将添加一个子项,该子项是根据用户输入值初始化的PersonDetail类的对象。当子项添加操作完成后,程序弹出如图7.51所示的信息对话框。进行多次添加操作后,在Name属性为"ReadFileName"的文本框控件输入任意XML文件的文件名,单击"输出XML"按钮,即可完成XML格式的序列化操作。可将序列化操作创建的XML文件名再次输入到Name属性为ReadFileName的文本框控件中,然后单击"XML反序列化"按钮即可完成该XML文件中数据的反序列化操作,运行结果如图7.52所示。
|
图7.51 以XML格式序列化 |
|
(点击查看大图)图7.52 反序列化XML文件并输出 |
根据输入值,创建的list对象数据已经被成功反序列化,并且在Name属性为"XmlList"的列表框控件中显示。
解析
反序列化是序列化的逆向操作,既然序列化操作需要写入到流,反序列化即读取流并将对象数据提取出来。例如,在程序集相同目录下有一个"Person.dat"文件,该文件以二进制格式持久化已知类型(如PersonName类)的对象,其反序列化的方法如以下代码所示:
//导入BinaryFormatter类的命名空间 using System.Runtime.Serialization.Formatters.Binary; //创建BinaryFormatter类对象MyBF BinaryFormatter MyBF = new BinaryFormatter(); //创建FileStream 类型引用fs,读取"Person.dat"文件 FileStream fs = new FileStream("Person.dat", FileMode.Open); //调用MyBF的DeSerialize方法,传递fs参数 PersonName Pn = (PersonName)MyBF.DeSerialize(fs); //关闭fs对象 fs.Close(); |
以上代码中,程序获得了PersonName类对象的引用Pn,该对象根据"Person.dat"文件反序列化创建,还原原对象的数据。
面试例题22:如何自定义序列化?
考点:理解自定义序列化,学习自定义序列化方法。
出现频率:★★★★
解答
在Visual Studio 2005/ Visual Studio 2008中创建一个C#的Windows窗体应用程序项目,并将其项目命名为CustomSerialize。程序可以根据用户输入值创建一个对象,以二进制格式序列化对象,并持久化到指定的路径。这个过程,对象的字段值将被添加一段字符串,并且其字段值对应的键名也被修改为指定值,而不是字段名称。在反序列化的过程中,对象的字段数据值被修改为原始值,所以看上去对象数据没有作任何修改。在Visual Studio 2005/Visual Studio 2008的"Form1.cs[设计]"视图中创建基本的窗体布局和控件,控件的命名如图7.53所示。
|
(点击查看大图)图7.53 自定义序列化窗体控件布局及命名 |