跟序列化相關的兩個類型:
SerializableAttribute:指示一個類是可以序列化的。
ISerializable:使對象可以自己控制其序列化和反序列化的過程。
列表比較三種序列化方法。
XML | SOAP | 二進制 | |
序列化器類 | XmlSerializer | SoapFormatter | BinaryFormatter |
SerializableAttribute 標記 | 不需要 | 需要 | |
ISerializable 接口 | 不需要實現,實現了也不起作用。 | 可以不實現,但實現了就起作用。 | |
無參構造函數 | 必須有,系統提供的缺省無參構造函數也算。 | 不需要,因爲反序列化時不調用構造函數。 | |
被序列化的數據成員 | 公共屬性和字段 | 所有 | |
產生文件大小 | 大 | 大 | 小 |
XML序列化的優點是使用簡單,也頗具靈活性,比如可以控制數據在 XML 文件中是作爲 Element 還是作爲 Attribute ,以及顯示的名稱等。XML 序列化對一般的應用是足以應付的,但當序列化循環引用的對象,即有多個引用指向同一個對象實體時,XML 序列化機制將在每個引用的地方都創建一個對象副本。這除了會導致數據存儲上的冗餘外,更嚴重的是使一個對象在反序列化後變成了毫無關係的多個對象,即 XML 反序列化後可能得到錯誤的對象關係圖表。比如下圖所示的簡單例子:
對應三種類型分別有三個對象 cObject 及其成員 _aObject、 _bObject,_aObject 由 cObject 構造,_bObject 中存的是對 _aObject 的引用,即 cObject 的成員 _aObject 和 _bObject 的成員 _aObject 是同一個東西。則 ClassC 類型的對象 cObject 經 XML 序列化的結果是:
<Name>ccc</Name>
<AObject>
<ID>37</ID>
<Name>aaa</Name>
</AObject>
<BObject>
<Name>bbb</Name>
<AObject>
<ID>37</ID>
<Name>aaa</Name>
</AObject>
</BObject>
</ClassC>
從這個結果反序列化後得到的新的 cObject,其成員 _aObject 跟 _bObject 中的 _aObject 可就是兩個對象了。要解決這個問題,我能想到的就是給對象加上 GUID 屬性,在反序列化後根據 GUID 屬性重新設置引用,不知還有沒有其它辦法。
SOAP 和二進制序列化的優點是可以精確地控制序列化及反序列化的過程,並可以序列化對象的非公共成員。所以對複雜對象的序列化,我們應該在實現 ISerializable 接口後,用 SOAP 或 二進制的方式保存數據。至於缺點,如果你嫌在類名上加個 Serializable 標記很麻煩的話,這也許算個缺點。
還是上面的例子,如果用 SOAP 序列化 cObject 對象,結果是:
<SOAP-ENV:Body>
<a1:ClassC id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializeTest.CrossReference/SerializeTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<ClassCNameName id="ref-3">ccc</ClassCNameName>
<ClassCAObjectAObject href="#ref-4"/>
<ClassCBObjectBObject href="#ref-5"/>
</a1:ClassC>
<a1:ClassA id="ref-4" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializeTest.CrossReference/SerializeTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<ClassANameName id="ref-6">aaa</ClassANameName>
<ClassAIDID>37</ClassAIDID>
</a1:ClassA>
<a1:ClassB id="ref-5" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializeTest.CrossReference/SerializeTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<ClassBNameName id="ref-7">bbb</ClassBNameName>
<ClassBAObjectAObject href="#ref-4"/>
</a1:ClassB>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
很明顯,裏面存的是對象引用,這是一個精確副本,反序列化後毫無問題。
附:ClassC 的一段代碼:
{
_name = "Unknown ClassC Object";
InitData();
}
private void InitData()
{
_aObject = new ClassA( 1 );
_aObject.Name = "aaa";
_aObject.ID = 37;
_bObject = new ClassB();
_bObject.Name = "bbb";
_bObject.AObject = _aObject;
}