C#对象序列化(2)

解析

对象序列化可以使对象的数据持久地保存,对象状态可以是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  自定义序列化窗体控件布局及命名

posted @ 2009-11-22 20:32  杨子宜  阅读(295)  评论(0编辑  收藏  举报