对象序列化在应聘过程中并不常见,但是在实际应用中却是很常见的。本节将列举3个比较典型的问题,以扩充.Net的相关知识面。
面试例题20:编写对象序列化的程序。
考点:掌握对象序列化的基本方法,理解序列化中不同格式的区别。
出现频率:★★★
解答
在Visual Studio 2005/Visual Studio 2008中创建一个C#的Windows窗体应用程序项目,并将其项目命名为MultiSerialize。程序使用5个"TextBox"控件分别接受用户的输入,其中上面4个控件用于传递相应的值给某个对象,而下面的"TextBox"控件用于存储文件名。当用户单击"TextBox"操作下面的3个按钮控件中任意一个时,程序将根据用户输入值初始化一组对象,并将对象图进行相应格式的序列化。在Visual Studio 2005/ Visual Studio 2008的"Form1.cs[设计]"视图中创建基本的窗体布局和控件,控件的命名如图7.43所示。
|
图7.43 对象序列化窗体控件的布局及命名 |
在编写代码前,先引用System.Runtime.Serialization. Formatters.Soap.dll程序集,即单击Visual Studio 2005/ Visual Studio 2008菜单栏的"项目|添加引用"命令,在".NET"选项卡中找到所需要的程序集,单击"确定"按钮,即完成了外部程序集的引用操作,结果如图7.44所示。
|
图7.44 引用外部程序集 |
分别双击Form1的3个按钮控件,编写相应的"Click"事件处理方法。MultiSerialize项目的Form1.cs如代码7.25所示。
代码7.25 序列化对象:Form1.cs
using System; ……………………………………… //导入必要的命名空间 using System.Xml.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Soap; using System.Runtime.Serialization; using System.IO;
namespace MultiSerialize { public partial class Form1 : Form { //声明string类型变量,用于方法中存储用户输入值 string n, nn, p, m, fn, Success; public Form1() { InitializeComponent(); }
private void XmlBtn_Click(object sender, EventArgs e) { //将用户输入值赋值给变量 n = this.Nm.Text; nn = this.NNm.Text; p = this.Pwd.Text; m = this.Msg.Text; fn = this.FileName.Text; //定义成功信息 Success = "XML输出成功"; //创建PersonName的对象引用Pn,并传递4个变量 PersonName Pn = new PersonName(n, nn, p, m); //创建XmlSerializer的对象引用Xs,并传递类型参数 XmlSerializer Xs = new XmlSerializer(typeof(PersonName), new Type[] { typeof(PersonOther) }); //创建Stream类型引用fs,并传递fn作路径参数 Stream fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.None); //调用Xs的Serialize方法,传递fs和Pn参数 Xs.Serialize(fs, Pn); //关闭fs对象 fs.Close(); //输出成功信息 MessageBox.Show(Success); }
private void BinBtn_Click(object sender, EventArgs e) { //创建IFormatter接口引用,来自于BinaryFormatter类对象 IFormatter Fmt = new BinaryFormatter(); //定义成功信息 Success = "二进制输出成功"; //调用UseIFormatter方法,并传递Fmt和Success参数 UseIFormatter(Fmt, Success); }
private void SoapBtn_Click(object sender, EventArgs e) { //创建IFormatter接口引用,来自于SoapFormatter类对象 IFormatter Fmt = new SoapFormatter(); //定义成功信息 Success = "Soap输出成功"; //调用UseIFormatter方法,并传递Fmt和Success参数 UseIFormatter(Fmt, Success); } //定义UseIFormatter方法,接收一个IFormatter类型参数 void UseIFormatter(IFormatter Fmt, string Success) { //将用户输入值赋值给变量 n = this.Nm.Text; nn = this.NNm.Text; p = this.Pwd.Text; m = this.Msg.Text; fn = this.FileName.Text; //创建PersonName的对象引用Pn,并传递4个变量 PersonName Pn = new PersonName(n, nn, p, m); //创建Stream类型引用fs,并传递fn作路径参数 Stream fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.None); //调用Fmt的Serialize方法,传递fs和Pn参数 Fmt.Serialize(fs, Pn); //关闭fs对象 fs.Close(); //输出成功信息 MessageBox.Show(Success); } }
//定义PersonName类,并标记可序列化特性 [Serializable] public class PersonName { public string _name; //标记不可序列化特性 [NonSerialized] public string _nickname; //创建PersonOther类型的对象Po public PersonOther Po = new PersonOther(); public PersonName() { } //重载构造函数,接收4个string类型参数 public PersonName(string n,string nn, string p, string m) { //赋值给字段 this._name = n; this._nickname = nn; //赋值给Po对象的两个属性 this.Po.Password = p; this.Po.Message = m; } }
//定义PersonOther类,并标记可序列化特性 [Serializable] public class PersonOther { //定义2个私有字段 string _password; string _message; //定义两个公共属性,可以读写相应的私有字段 public string Password { get { return _password; } set { _password = value; } } public string Message { get { return _message; } set { _message = value; } } public PersonOther() { } } } |
程序运行时,可以向前4个"TextBox"控件填入某人的基本资料,然后在Name属性为"FileName"控件中填入"Person.dat",即输出文件的文件名,运行结果如图7.45所示。
当用户单击"二进制格式"按钮后,程序将根据用户输入的值创建并初始化1个PersonName类的对象。程序使用二进制格式序列化该对象并输出到程序集相同目录下的Person.dat文件中,当完成了这些工作,程序将使用信息对话框提示序列化操作成功,运行结果如图7.46所示。
|
图7.45 输入多个信息 |
|
(点击查看大图)图7.46 以二进制格式序列化 |
在图7.47中,编译完成的MultiSerialize程序集相同目录下已经创建了Person.dat文件,并且程序也跳出了信息对话框,并提示操作成功。如果按下"Soap格式"按钮或"XML格式"按钮,其运行结果是相似的,但是建议其生成文件的扩展名应分别修改为对应的"soap"和"xml"。假设用户已经生成了3种不同格式的文件,对象数据通过序列化操作持久化到文件中了。用Visual Studio 2005/Visual Studio 2008打开"Person.dat"文件如图7.47所示。
|
(点击查看大图)图7.47 "Person.dat"文件内容 |
其中中文部分无法正常显示,但是数字是可以正常显示,这是因为Name属性中"Pwd"的控件的值是数字(即图中的"1900")。在程序运行时填入英文,同样可以正常显示。从"Person.dat"文件的内容中可以看出,二进制格式保存了比较完整的对象信息,如所属程序集,程序集版本等信息,有利于以后还原对象数据。用Visual Studio 2005/Visual Studio 2008打开"Person.soap"文件,结果如图7.48所示。
|
(点击查看大图)图7.48 "Person.soap"文件内容 |
从图7.48中可看到_nickname字段并没有被持久化,因为该字段在Person类中定义时标记了[NonSerialized]特性,即不可序列化。相应地,用Visual Studio 2005/ Visual Studio 2008打开"Person.xml"文件,结果如图7.49所示
|
(点击查看大图)图7.49 "Person.xml"文件内容 |
说明:项目所生成的程序集路径为"项目路径\bin\debug"。
如果修改程序的代码,可知二进制格式序列化的文件除了保存程序集的信息,还保存私有字段及公共字段的数据SOAP格式和XML格式仅保存对象的公共字段及公共属性,但是这两种格式可以应用于更大范围的平台、应用程序框架中。并且XML格式所序列化的对象数据包含了被标记为[NonSerialized]特性的字段。
说明:本题程序采用IFormatter接口实现多态的应用,使二进制格式化和SOAP格式化操作重用了部分的代码。程序中使用IFormatter接口必须导入其命名空间,即System.Runtime.Serialization。