using System; …………………………………………… //导入必要的命名空间 using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; using System.IO; namespace CustomSerialize { public partial class form1 : Form { //声明string类型变量,用于存储用户确认的路径 string fn; public form1() { InitializeComponent(); } private void form1_Load(object sender, EventArgs e) { //窗体载入时,下面两个GroupBox容器控件不可用 this.groupBox2.Enabled = false; this.groupBox3.Enabled = false; } private void PathBtn_Click(object sender, EventArgs e) { //获取PathTxt的Text属性值,并赋值给fn变量和PathLabel的Text属性值 fn = PathTxt.Text; PathLabel.Text = fn; //下面两个GroupBox容器控件可用 this.groupBox1.Enabled = false; this.groupBox2.Enabled = true; //第1个GroupBox容器控件可用 this.groupBox3.Enabled = true; } private void SerBtn_Click(object sender, EventArgs e) { //创建Details类对象Dt Details Dt = new Details(); //将两个文本框内容复制给Dt的两个属性 Dt.Name = nametxt.Text; Dt.NickName = nicknametxt.Text; //创建IFormatter接口引用,来自于BinaryFormatter类对象 IFormatter Fmt = new BinaryFormatter(); //创建Stream类型引用fs,并传递fn作路径参数 Stream fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.None); //调用Fmt的Serialize方法,传递fs和Dt参数 Fmt.Serialize(fs, Dt); //关闭fs对象 fs.Close(); //输出成功信息 MessageBox.Show("序列化完成"); } private void DeserBtn_Click(object sender, EventArgs e) { try { //创建FileStream类型引用fs,并传递fn作路径参数,文件模式为打开文件 FileStream fs = new FileStream(fn, FileMode.Open); //创建BinaryFormatter类对象Fmt IFormatter Fmt = new BinaryFormatter(); //调用Fmt对象Deserialize方法,传递fs //将fs流中的对象转换为Details类型,并将引用赋值给Dt Details Dt = (Details)Fmt.Deserialize(fs); //将Dt属性值赋值给以下两个控件的Text属性值 nametxtnew.Text = Dt.Name; nicknametxtnew.Text = Dt.NickName; } //捕捉并显示文件未找到异常 catch (FileNotFoundException ex) { string str = String.Format("异常信息:{0}", ex.Message); MessageBox.Show(str); } } } [Serializable] //定义Details类,实现ISerializable接口 public class Details : ISerializable { string _name; string _nickname; //定义两个公共属性,可以读写相应的私有字段 public string Name { get { return _name; } set { _name = value; } } public string NickName { get { return _nickname; } set { _nickname = value; } } public Details() { } //重载构造函数,接收两个参数 private Details(SerializationInfo inputinfo, StreamingContext sc) { int i; //获取"Person Name"名称的值赋值给_name字段 _name = inputinfo.GetString("Person Name"); //获取_name字段内容中'>'字符索引值加1的整数值 i = _name.IndexOf('>') + 1; //截取索引i开始的字符串内容,并赋值给_name字段 _name = _name.Substring(i); //获取"Person Name"名称的值赋值给_name字段 _nickname = inputinfo.GetString("Person NickName"); //获取_name字段内容中'>'字符索引值加1的整数值 i = _nickname.IndexOf('>') + 1; //截取索引i开始的字符串内容,并赋值给_nickname字段 _nickname = _nickname.Substring(i); } //实现ISerializable接口的GetObjectData方法,接收两个参数 void ISerializable.GetObjectData(SerializationInfo outputinfo, StreamingContext sc) { //修改所需序列化的字段值,并修改名称,填充到outputinfo对象 outputinfo.AddValue("Person Name", "The boy's name is -->" + _name); outputinfo.AddValue("Person NickName", "The boy's nickname is -->" + _nickname); } } } |
程序运行后,除了Text属性为"文件路径"的容器(GroupBox容器控件)中的控件外,窗体大部分控件为不可用状态。输入文件名到该容器的文本框控件中(即路径为程序集当前目录),结果如图7.54所示。
单击"确定路径"按钮后,窗体中其他控件均为可用状态,而Text属性为"文件路径"的容器中的控件变为不可用状态。接着在Text属性为"原始对象数据"的容器中的文本框控件中,输入2个字符串值,并单击"序列化"按钮,结果如图7.55所示。
|
(点击查看大图)图7.54 输入文件路径 |
|
(点击查看大图)图7.55 完成序列化 |
首先程序创建Details类的对象,并根据输入值对该对象进行了初始化操作。然后将该对象进行序列化操作,自动调用Details类中实现的 GetObjectData()方法,修改字段值,并指定了相应的键值。在程序集所在的目录下已经创建了BoyName.dat文件,在Visual Studio 2005/Visual Studio 2008中打开"BoyName.dat"文件,如图7.56所示。
|
(点击查看大图)图7.56 "BoyName.dat"文件内容 |
从图7.56中可看出,首先原始对象的字段数据进行成功的修改,_name字段值前面加上了"The boy's name is",_nickname字段值前面加上"The boy's nickname is"。其次,字段的名称没有被持久化到dat文件中,其对应键值分别被修改为"Person Name"和"Person NickName"。最后,单击"反序列化"按钮,结果如图7.57所示。
|
(点击查看大图)图7.57 完成反序列化并输出 |
创建Details类的新对象,其字段数据和原始对象一致,这说明其重载构造函数的操作成功。Details类的重载构造函数序列化时修改的部分进行了逆向操作,还原了原始对象的数据。
解析
一般情况下,序列化的过程细节并不需要考虑,但是在开发程序时要对序列化的过程进行微操作,可以使用自定义序列化完成。常用的自定义序列化方法在定义可序列化的类型时,使之实现using System.Runtime.Serialization..ISerializable接口,当实现该接口后,序列化该类对象的过程将自动调用该类实现的GetObjectData()方法,开发者所需的微操作可以定义在这个GetObjectData()方法中。相应地,在反序列化的过程中也可以对过程进行干预,反序列化是根据持久化的数据信息重新创建该类的对象。实现ISerializable接口的类将使用重载的构造函数创建新对象,可以将干预反序列化过程的代码编写在该类重载构造函数中,如以下代码所示:
public class Details : ISerializable { public Details() { } //重载构造函数,接收两个指定类型的参数 private Details(SerializationInfo inputinfo, StreamingContext sc) { 干预反序列化过程的微操作代码; } //实现ISerializable接口的GetObjectData方法,接收2个参数 void ISerializable.GetObjectData(SerializationInfo outputinfo, StreamingContext sc) { //修改所需序列化的字段值,并填充到outputinfo对象 outputinfo.AddValue("键名", 对序列化字段的修改值); } } |
以上代码中,重载构造函数必须接收指定类型的参数,而实现接口的GetObjectData()方法,调用outputinfo参数的AddValue()方法,即可对序列化过程的数据进行微操作。这个AddValue()方法有多个重载版本,这里用于修改字段值。