分析问题
在其他很多编程语言(比如C++)中,实现序列化和反序列化的过程往往需要程序员手动地编写代码。而.NET中提供的Serializable特性却帮助程序员非常便捷地申明一个可序列化的类型。但是,便捷的同时往往意味着缺乏灵活性。很多时候,由于业务逻辑的要求,程序员往往需要主动地控制序列化和反序列化的过程,.NET提供了ISerializable接口来满足自定义序列化需求。
为了实现自定义的序列化和反序列化,程序员需要实现ISerializable接口,并且提供一个特定的在反序列化中构造对象的方法。下面的代码展示了自定义序列化和反序列化的类型模型:
class MyObject : ISerializable { protected MyObject(SerializationInfo info, StreamingContext context) { //实现反序列化 } public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { //实现序列化 } }
正如读者所看到的,特定地实现反序列化的构造方法可以不是公共的构造方法。事实上,该构造方法的可访问级别并不影响反序列化,程序员可以根据需求把它定义为private和protected,通常情况下由于安全的关系,该构造方法不会被申明为public,这也是.NET小组没有把反序列化设计成通过接口来实现的原因。GetObjectData和特殊构造方法都接受两个参数,SerializationInfo类型参数的作用类似于一个哈希表,通过键/值对来存储整个对象的内容,而StreamingContext类型参数则包含了流的当前状态,程序员可以根据此参数来判断是否需要序列化和反序列化类型对象。
如果基类实现了ISerializable接口,则派生类型需要针对自己的成员实现反序列化构造方法,并且重写基类中的GetObjectData方法。以下代码给出了一个具体的实例。
using System; using System.Text; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace TestCustomize { //首先编写一个需要被序列化和反序列化的类型,这里需要注意的是,该类型将被其他类型继承 [Serializable] class MyObject : ISerializable { private int _MyInt; [NonSerialized] private string _MyString; public MyObject(int i, string s) { _MyInt = i; _MyString = s; } public override string ToString() { return new StringBuilder().AppendFormat("整数是:{0}\r\n字符串是:{1}\r\n",_MyInt.ToString(),_MyString).ToString(); } //实现自定义的反序列化 protected MyObject(SerializationInfo info, StreamingContext context) { //从SerializationInfo结构中读取内容 _MyInt = info.GetInt32("MyObjectInt"); _MyString = info.GetString("MyObjectString"); } //实现自定义的序列化 public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { //写入SerializationInfo结构 info.AddValue("MyObjectInt", _MyInt); info.AddValue("MyObjectString", _MyString); } } //随后编写一个继承该类型的子类,并且添加一个私有的成员变量。子类必须负责序列化和反序列化自己添加的成员。 [Serializable] class MyObjectSon : MyObject { private string _SonString; public MyObjectSon(int i, string s) : base(i, s) { _SonString = s; } public override string ToString() { return new StringBuilder().AppendFormat("{0}子类的字符串是:{1}\r\n",base.ToString(),_SonString).ToString(); } //实现反序列化,只负责子类添加的成员 protected MyObjectSon(SerializationInfo info, StreamingContext context):base(info,context) { _SonString = info.GetString("MyObjectSonString"); } //实现序列化,只负责子类添加的成员 public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("MyObjectSonString", _SonString); } } //最后编写入口方法,对这两个类型对象进行序列化和反序列化。 class CustomizeSerialization { //main方法,测试自定义的序列化和反序列化 static void Main() { MyObjectSon obj = new MyObjectSon(10, "我是字符串"); Console.WriteLine("初始对象:{0}", obj); byte[] data = Serialize(obj); Console.WriteLine("经过序列化和反序列化后:"); Console.WriteLine(DeSerialize(data)); Console.Read(); } //序列化对象 static byte[] Serialize(MyObjectSon obj) { IFormatter formatter = new BinaryFormatter(); using (MemoryStream ms=new MemoryStream ()) { formatter.Serialize(ms, obj); return ms.ToArray(); } } //反序列化对象 static MyObjectSon DeSerialize(byte[] bytes) { IFormatter formatter = new BinaryFormatter(); using (MemoryStream ms=new MemoryStream (bytes)) { return formatter.Deserialize(ms) as MyObjectSon; } } } }
由于手动地控制了序列化和反序列化,实现了类型中所有成员的序列化,所有类型的对象经过序列化和反序列化后被完整地还原了,包括被申明了NonSerialized特性的成员。下面是代码的执行结果。
注意
实现子类的序列化和反序列化方法需要调用基类的序列化和反序列化实现,否则对象将无法被成功地序列化或者反序列化。
答案
通过实现ISerialization接口中的GetObjectData方法可以实现自定义的序列化,而通过添加带有SerializationInfo和StreamingContext参数的构造方法可以自定义反序列化的过程。
如何自定义序列化和反序列化