上一篇中,讨论了序列化和反序列化的基础,同时简单说明了如何进行单个类的序列化和反序列化。
一般说来,使用默认的BinaryFormatter已经可得到很高效的字节流了。但是,实际应用中,由于部分数据可能无法被默认的模式自动序列化,自定义序列化就成为必不可少的一部分。自定义序列化的在进行XML序列化的时候尤其重要。因为XML序列化用的是浅序列化模式,也就是说,只有标记为public的公共属性会被序列化,而private的部分则不会被序列化到。通过自定义序列化,我们可以让XML序列化的时候,同时包含我们重建实例所需要的所有参数。dotNET是通过提供了ISerializable接口来达到实现自定义序列化的目的的。
namespace SimulationEnv
...{
class Program
...{
static void Main(string[] args)
...{
BinaryFormatter formatter = new BinaryFormatter();
Stream s = new MemoryStream();
Player p = new Player("李逍遥");
// 序列化
formatter.Serialize(s, p);
byte[] sav = ((MemoryStream)s).ToArray();
Output(sav);
Console.Write(" ");
Console.ReadLine();
// 反序列化
s.Position = 0;
Player psav = (Player)formatter.Deserialize(s);
Console.WriteLine(psav.Name);
Console.ReadLine();
}
private static void Output(byte[] data)
...{
for (int i = 0; i < data.Length; i++)
...{
string str = data[i].ToString("X");
if (str.Length == 1)
...{
str = "0" + str;
}
Console.Write(str + " ");
if ((i + 1) % 16 == 0)
...{
Console.Write(" ");
}
}
}
}
[Serializable]
public class Player : ISerializable
...{
public string Name;
public Player(string Name)
...{
this.Name = Name;
}

ISerializable Members#region ISerializable Members
// 该构造函数将在反序列化时候使用
private Player(SerializationInfo info, StreamingContext context)
...{
Name = info.GetString("Name");
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
...{
info.AddValue("Name", Name);
}
#endregion
}
}这里,构造函数Player(SerializationInfo info, StreamingContext context)被用来在反序列化时构造出和原实例完全一样的实例。由于这个构造函数的特殊性,为了防止别的程序有意无意的调用,该构造函数可以声明为private或者protected而不会影响系统本身对它的调用。也就是说,如果是一个已经封装的类,应该声明为private,未封装的类,应该声明为protected,来避免可能造成的系统安全隐患。
有趣的是,根据自定义序列化,我们可以序列化一个类以后,在反序列化的时候,将其反序列化为另外一个类。
namespace SimulationEnv
...{
class Program
...{
static void Main(string[] args)
...{
BinaryFormatter formatter = new BinaryFormatter();
Stream s = new MemoryStream();
Player p = new Player("李逍遥");
// 序列化
formatter.Serialize(s, p);
byte[] sav = ((MemoryStream)s).ToArray();
Output(sav);
Console.Write(" ");
Console.ReadLine();
// 反序列化
s.Position = 0;
TeamPlayer tp = (TeamPlayer)formatter.Deserialize(s);
Console.WriteLine(tp.Name);
Console.ReadLine();
}
private static void Output(byte[] data)
...{
for (int i = 0; i < data.Length; i++)
...{
string str = data[i].ToString("X");
if (str.Length == 1)
...{
str = "0" + str;
}
Console.Write(str + " ");
if ((i + 1) % 16 == 0)
...{
Console.Write(" ");
}
}
}
}
[Serializable]
public class Player : ISerializable
...{
public string Name;
public Player(string Name)
...{
this.Name = Name;
}

ISerializable Members#region ISerializable Members
// 该构造函数将在反序列化时候使用
private Player(SerializationInfo info, StreamingContext context)
...{
Name = info.GetString("Name");
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
...{
info.AddValue("Name", Name);
info.SetType(typeof(TeamPlayer));
}
#endregion
}
[Serializable]
public class TeamPlayer
...{
public string Name;
}
}通过info.SetType(),可以自定义在反序列化的时候,将其反序列化为某个特定的类。这点特性在某些时候很有用。比如,在进行系统整合的时候,服务端有一个类Product,且这个类是已经包装好的,不可以更改。但是客户端只能够发送Service的类。这时候,就可以用自定义的序列化,在序列化的时候,将Service类直接映射为Product类。这样,服务端进行反序列化的时候,得到的是Product类而不是Service。当然,这也需要服务端是使用反序列化来获得Product类的情况才可以。
本文深入探讨了.NET中自定义序列化的过程与实践,重点介绍了如何利用ISerializable接口实现对象的状态保存与恢复,以及如何通过info.SetType()方法改变反序列化的目标类型。

被折叠的 条评论
为什么被折叠?



