浅析 .NET 中的 Serialization
【IT168 技术文档】
本文简要介绍了.NET中的序列化(Serialization)概念,以及在代码中实作Serialization的方法。文章的最后介绍了Serialization在Clone方法中的运用。
Serialization的概念
Serialization是.NET中一种实现对象持久性(Persistent)的机制。它是一个将对象中的数据转换成一个单一元素(通常是Stream)的过程。它的逆过程是Deserialization。Serialization的核心概念是将一个对象的所有数据看作一个独立的单元。
一般说来,在两种情况下非常需要Serialization:
1)当我们希望能够将对象当前的状态完整地保存到存储介质中,以便我们以后能够精确地还原对象时;
2)当我们希望将对象从一个应用程序空间(Application domain)传递到另一个应用程序空间时。例如,Windows Form程序就是利用Serialization机制来实现剪贴板的copy & paste的。
.NET Framework支持两种类型的Serialization:Shallow Serialization和Deep Serialization。
所谓Shallow Serialization是将对象的可读写(read-write)属性的值转换成字节流,而对象内部的数据(没有通过read-write属性暴露出来的数据)则不被转换。XmlSerializer以及Web Services就使用这种技术。
Deep Serialization比Shallow Serialization更加彻底,因为它是将存储在对象私有变量里的实际值拷贝到字节流里。而且Deep Serialization还将serialize整个object graph。也就是说,如果你的对象持有其他对象的引用,或者其他对象引用的集合,那么所有这些对象都将被Serialize。BinaryFormatter和SoapFormatter以及.NET Remoting都使用Deep Serialization技术,它甚至被有限地用于LosFormatter来产生存储在Web Form页中的状态数据。
本文将着重于Deep Serialization。
Serialization的过程
.NET Framework通过Reflection提供自动Serialization的机制。当一个对象被序列化(Serialized)的时候,它的类名,Assembly,以及类实例的所有数据成员都将被写入存储介质中。Serialization引擎保持对所有已经被序列化的对象引用的追踪,以确保相同的对象引用最多只被序列化一次。
通常,一个Serialization过程会由formatter(例如BinaryFormatter)的Serialize方法引发。对象的Serialization过程按照以下规则进行:
1、检测以确保formatter是否拥有一个代理选择器(surrogate selector)。如果有,检查代理选择器是否持有给定的对象类型。如果有,ISerializable.GetObjectData被调用。
2、如果formatter没有代理选择器,或者代理选择器没有对象类型,检查对象是否被用Serializable属性标记。如果没有,则抛出SerializationException异常。
3、如果对象被标记为Serializable,检查对象是否实现了ISerializable接口。如果实现了此接口,则GetObjectData被调用。
4、如果对象没有实现ISerializable接口,则使用默认的序列化策略,来序列化没有用NonSerialized属性标记的域。
使你的Class能够被序列化
通过上面对Serialization过程的分析,我们可以看出,有两种方式可以使一个class能够被序列化:
1)将此class简单地标记为Serializable;
2)为此class实现ISerializable接口,并将此class标记为Serializable。
1、标记Serializable属性
标记Serializable属性的方式是实现Serialization的基本方法。举个简单的例子:
[Serializable]
public class Person
{
public string name = null;
public int age = 0;
}
你可以使用BinaryFormatter来将上面的class序列化:
Person sam = new Person();
sam.name = "sam";
sam.age = 24;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("sam.dat",
FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, sam);
stream.Close();
就是这么简单,你所要做的就是创建一个Stream和一个formatter的实例,然后调用formatter的Serialize方法。经过BinaryFormatter serialize的数据仍然能够通过BinaryFormatter deserialize回来,方法与serialize同样简单,这里就不赘述了。
如果你不想将类里的所有域都序列化,可以使用NonSerialized属性进行选择。如:
[Serializable]
public class Person
{
public string name = null;
[NonSerialized]
public int age = 0;
}
这样,age域就不会被序列化了。
需要注意的是,Serializable属性并不能被继承。也就是说如果你希望Person的派生类也能够被Serialize的话,那么这个派生类也必须被Serializable标记。否则将得到SerializationException异常。
同样的,Person类中的所有对其他类的引用,其所引用的类都应该是能够被Serialize的。.NET Framework中的大部分class都实现了ISerializable接口,但有些class没有实现,例如ImageList。可以通过MSDN Library的到一个实现了ISerializable接口的class列表。对那些没有实现此接口的class,使用的时候要当心。