也说串行化
串行化(serialization)是指将一个对象的当前状态转换成字节流(a stream of bytes)的过程,而反串行化(deserialization)则指串行化过程的逆过程,将字节流转换成一个对象。初听起来可能对此不太感兴趣,但是使用串行化却有许多重要的原因。一旦将某一对象串行化,得到的字节可以存储在文件、数据库,或内存中——只要是可以存储的任何地方。需要恢复对象时,仅仅只需从它存储的位置反串行化即可。对象固有的这种特性对于无状态的Web应用程序是非常重要的,因为它允许重要的状态信息可以在用户请求之间保留。
串行化也允许对象在应用程序边界之间传递,编程人员可以将描述对象状态的字节流在网络上传递,并在另一端反串行化成一个匹配的对象。从本质上讲,串行化允许对象以“数值”的方式传递给另一个应用程序。.NET远程框架广泛地在进程之间使用串行化来编组对象数据。
串行化不是.NET新引入的概念,Windows程序员同各种各样的串行化打交道:MFC中的归档机制、Visuabl Basic的PropertyBag和COM的IPersistStream接口等,都具有类似的特性。那么.NET串行化与这些方式又有什么区别呢?一个字,简单。在.NET以前的开发环境中,串行化是一个手工定制,对于类设计人员来说,实现类的串行化并不困难,但是实现难度较大的设计,它就显得能麻烦、耗时。在.NET中,该过程完全是自动进行的,用户所做的就是使用Serializable特性来标记一个能串行化的类,运行库做余下的事。
.NET运行库怎样实现这一捷径的呢?当然是利用镜像,或更为准确地说是反射。前面提到,程序集含有名为元数据的自描述(self-describing)数据,程序集的元数据完整地表述了内部每一个类型,包括类型中每个私有字段的信息。通过反射过程,运行库能识别元数据,并用它来决定类应怎样被串行化。
使用Serializable特性实现串行化:
前面已经提到,如果允许运行库串行化一个对象,必须将该类标记上Serializable特性。下面的代码定义了两个标记了Serialable特性的类。
[Serializable]
public class Car
{
private string mColor;
private int mTopSpeed;
//Reference to another object
private Radio mRadio;
[NonSerialized] //Runtime will not serialize this field
private string mNickName;
public Car(string nickName, int topSpeed, string color)
{
mNickName = nickName;
mTopSpeed = topSpeed;
mColor = color;
mRadio = new Radio();
}
}
[Serializable]
public class Radio
{
private int mVolume = 5;
}
如上所示,Car类的mNickName字段用NonSerialized特性标注,指示运行库进行串行化时跳过该项,而其他部分允许进行串行化。这里注意,mRadio是对另一对象的引用,意味着运行库必须对其串行化。事实上,当运行库被告知要对某个对象串行化时,使用给定的对象作为根,建立一个对象图表(类似于垃圾回收器)。所有在图表中的对象也必须可被串行化的,否则运行库将引发异常。
下面的代码举例说明如何将Car对象串行化到一个文件之中。
static void Main(string[] args)
{
Car myCar = new Car("Christine", 150, "Red");
FileStream mySoapFile = File.Create("Car.txt");
// Use a SOAP formatter object to serialize the object.
new SoapFormatter().Serialize(mySoapFile, myCar);
mySoapFile.Close();
}
运行库不控制串行化数据的实际格式,但是有一个专门进行格式化的对象负责对串行化输出进行格式化。.NET框架有两种格式化程序:SOAP格式化程序和二进制格式化程序。在前面的例子中,SoapFormatter对象将对象串行化进一个SOAP报文,并将其发送到特定的流中。图2-26显示了在执行完这段代码后Car.txt中的内容。
如果仔细查看图2-26,很容易发现SOAP是如何描绘每个对象和它的数据成员。这种方式通过增加文件大小来提高了可读性。二进制格式化程序使用了更为紧凑的格式。如果需要在网络间发送对象,这种方式就更重要,尤其是在需要考虑网络带宽而不强调可读性的情况下。
通过调用SoapFormatter.Dewerialize方法,就可将该对象读入内存中:
static void Rehydrate()
{
FileStream mySoapFile = File.Open("Car.txt", FileMode.Open);
Car myCar = (Car) new SoapFormatter().Deserialize(mySoapFile);
mySoapFile.Close();
}
图2-26 使用SOAP格式化程序对Car对象进行串行化
注:摘自《NET分布式编程——C#篇 》