NET中對象序列化研究論文
時間:2022-09-17 05:34:00
導語:NET中對象序列化研究論文一文來源于網友上傳,不代表本站觀點,若需要原創文章可咨詢客服老師,歡迎參考。
摘要實現序列化最重要的兩個原因是:將對象的狀態保存在存儲媒體中以便以后重新創建出完全相同的副本;按值將對象從一個應用程序域發送至另一個應用程序域。例如,序列化可用于在中保存會話狀態;將對象復制到Windows窗體的剪貼板中;它還可用于按值將對象從一個應用程序域遠程傳遞至另一個應用程序域。本文簡要介紹了中使用的序列化。
關鍵詞.net;序列化;封送
1引言
序列化是指將對象實例的狀態存儲到存儲媒體的過程。在此過程中,先將對象的公共字段和私有字段以及類的名稱(包括類所在的程序集)轉換為字節流,然后再把字節流寫入數據流。在隨后對對象進行反序列化時,將創建出與原對象完全相同的副本。
在面向對象的環境中實現序列化機制時,必須在易用性和靈活性之間進行一些權衡。只要對此過程有足夠的控制能力,就可以使該過程在很大程度上自動進行。例如,簡單的二進制序列化不能滿足需要,或者,由于特定原因需要確定類中哪些字段需要序列化。以下各部分將探討.NET框架提供的可靠的序列化機制,并著重介紹如何根據需要自定義序列化過程。
2持久存儲
我們經常需要將對象的字段值保存到磁盤中,并在以后在內存中還原次對象。盡管不使用序列化也能完成這項工作,但這種方法通常很繁瑣而且容易出錯,并且在需要跟蹤對象的層次結構時,會變得越來越復雜??梢韵胂笠幌戮帉懓罅繉ο蟮拇笮蜆I務應用程序的情形,程序員不得不為每一個對象編寫代碼,以便將字段和屬性保存至磁盤以及從磁盤還原這些字段和屬性。序列化提供了輕松實現這個目標的快捷方法。
公共語言運行時(CLR)管理對象在內存中的分布,.NET框架則通過使用反射提供自動的序列化機制。對象序列化后,類的名稱、程序集以及類實例的所有數據成員均被寫入存儲媒體中。對象通常用成員變量來存儲對其它實例的引用。類序列化后,序列化引擎將跟蹤所有已序列化的引用對象,以確保同一對象不被序列化多次。.NET框架所提供的序列化體系結構可以自動正確處理對象圖表和循環引用。對對象圖表的惟一要求是,由正在進行序列化的對象所引用的所有對象都必須標記為Serializable。否則,當序列化程序試圖序列化未標記的對象時將會出現異常。當反序列化已序列化的類時,將重新創建該類的對象,并自動還原所有數據成員的值。
3按值封送
按值封送是指將對象序列化為字節流,并從一個應用程序域傳輸至另一個應用程序域,然后進行反序列化,從而在第二個應用程序域中產生出該對象的一個副本,這在COM技術中經常提到。在.Net中,對象僅在創建對象的應用程序域中有效,除非對象是從MarshalByRefObject派生得到或標記為Serializable,否則,任何將對象作為參數傳遞或作為結果返回到另外一個應用程序域都將失敗。
如果對象標記為Serializable,則該對象將被自動序列化,并從一個應用程序域傳輸至另一個應用程序域,然后進行反序列化,從而在第二個應用程序域中產生出該對象的一個精確副本。
如果對象是從MarshalByRefObject派生得到,則從一個應用程序域傳遞至另一個應用程序域的是對象引用,而不是對象本身。也可以將從MarshalByRefObject派生得到的對象標記為Serializable。遠程使用此對象時,負責進行序列化并已預先配置為SurrogateSelector的格式化程序將控制序列化過程,并用一個替換所有從MarshalByRefObject派生得到的對象。如果沒有預先配置為SurrogateSelector,序列化體系結構將遵從下面的標準序列化規則。
4基本序列化
要使一個類可序列化,最簡單的方法是使用Serializable屬性對它進行標記,如下所示:
[Serializable]
publicclassMyObject
{
publicintn1=0;
publicintn2=0;
publicStringstr=null;
}
以下代碼片段說明了如何將此類的一個實例序列化為一個文件:
MyObjectobj=newMyObject();
obj.n1=1;
obj.n2=24;
obj.str="一些字符串";
IFormatterformatter=newBinaryFormatter();
Streamstream=newFileStream("MyFile.bin",FileMode.Create,FileAccess.Write,FileShare.None);
formatter.Serialize(stream,obj);
stream.Close();
本例使用二進制格式化程序進行序列化。只需創建一個要使用的流和格式化程序的實例,然后調用格式化程序的Serialize方法。流和要序列化的對象實例作為參數提供給此調用。類中包括private變量的所有成員變量,都將被序列化,但這一點在本例中未明確體現出來。在這一點上,二進制序列化不同于只序列化公共字段的XML序列化程序。
將對象還原到它以前的狀態也非常容易。首先,創建格式化程序和流以進行讀取,然后讓格式化程序對對象進行反序列化。以下代碼片段說明了如何進行此操作。
IFormatterformatter=newBinaryFormatter();
Streamstream=newFileStream("MyFile.bin",FileMode.Open,FileAccess.Read,FileShare.Read);
MyObjectobj=(MyObject)formatter.Deserialize(fromStream);
stream.Close();
//下面是證明
Console.WriteLine("n1:{0}",obj.n1);
Console.WriteLine("n2:{0}",obj.n2);
Console.WriteLine("str:{0}",obj.str);
上面所使用的BinaryFormatter效率很高,能生成非常緊湊的字節流。所有使用此格式化程序序列化的對象也可使用它進行反序列化,對于序列化將在.NET平臺上進行反序列化的對象,此格式化程序是一個理想的工具。需要注意的是,對對象進行反序列化時并不調用構造函數。對反序列化添加這項約束,是出于性能方面的考慮。但是,這違反了對象編寫者通常采用的一些運行時約定,因此,開發人員在將對象標記為可序列化時,應確??紤]了這一特殊約定。
如果要求具有可移植性,應該使用SoapFormatter。所要做的更改只是將以上代碼中的格式化程序換成SoapFormatter,而Serialize和Deserialize調用不變。對于上面使用的示例,該格式化程序將生成以下結果。
<SOAP-ENV:Envelope
xmlns:xsi=http:///2001/XMLSchema-instancexmlns:xsd="http:///2001/XMLSchema"
xmlns:SOAP-ENC=http:///soap/encoding/
xmlns:SOAP-ENV=http:///soap/envelope/
SOAP-ENV:encodingStyle="http:///soap/encoding/clr/1.0http:///soap/encoding/"xmlns:a1="http:///clr/assem/ToFile">
<SOAP-ENV:Body>
<a1:MyObjectid="ref-1">
<n1>1</n1>
<n2>24</n2>
<strid="ref-3">一些字符串</str>
</a1:MyObject>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
需要注意的是,無法繼承Serializable屬性。如果從MyObject派生出一個新的類,則這個新的類也必須使用該屬性進行標記,否則將無法序列化。例如,如果試圖序列化以下類實例,將會顯示一個SerializationException,說明MyStuff類型未標記為可序列化。
publicclassMyStuff:MyObject
{
publicintn3;5選擇性序列化
類通常包含不應被序列化的字段。例如,假設某個類用一個成員變量來存儲線程ID。當此類被反序列化時,序列化此類時所存儲的ID對應的線程可能不再運行,所以對這個值進行序列化沒有意義??梢酝ㄟ^使用NonSerialized屬性標記成員變量來防止它們被序列化,如下所示:
[Serializable]
publicclassMyObject
{
publicintn1;
[NonSerialized]publicintn2;
publicStringstr;
}
6自定義序列化
可以通過在對象上實現ISerializable接口來自定義序列化過程。這一功能在反序列化后成員變量的值失效時尤其有用,但是需要為變量提供值以重建對象的完整狀態。要實現ISerializable,需要實現GetObjectData方法以及一個特殊的構造函數,在反序列化對象時要用到此構造函數。以下代碼示例說明了如何在前一部分中提到的MyObject類上實現ISerializable接口。
[Serializable]
publicclassMyObject:ISerializable
{
publicintn1;
publicintn2;
publicStringstr;
publicMyObject(){}
protectedMyObject(SerializationInfoinfo,StreamingContextcontext)
{
n1=info.GetInt32("i");
n2=info.GetInt32("j");
str=info.GetString("k");
}
publicvirtualvoidGetObjectData(SerializationInfoinfo,StreamingContextcontext)
{
info.AddValue("i",n1);
info.AddValue("j",n2);
info.AddValue("k",str);
}
}
在序列化過程中調用GetObjectData時,需要填充方法調用中提供的SerializationInfo對象。只需按名稱/值對的形式添加將要序列化的變量。其名稱可以是任何文本。只要已序列化的數據足以在反序列化過程中還原對象,便可以自由選擇添加至SerializationInfo的成員變量。如果基對象實現了ISerializable,則派生類應調用其基對象的GetObjectData方法。
需要強調的是,將ISerializable添加至某個類時,需要同時實現GetObjectData以及特殊的構造函數。如果缺少GetObjectData,編譯器將發出警告。但是,由于無法強制實現構造函數,所以,缺少構造函數時不會發出警告。如果在沒有構造函數的情況下嘗試反序列化某個類,將會出現異常。
在反序列化過程中,使用出于此目的而提供的構造函數將SerializationInfo傳遞給類。對象反序列化時,對構造函數的任何可見性約束都將被忽略,因此,可以將類標記為public、protected、internal或private。通常,在類未封裝的情況下,將構造函數標記為protect。如果類已封裝,則應標記為private。要還原對象的狀態,只需使用序列化時采用的名稱,從SerializationInfo中檢索變量的值。如果基類實現了ISerializable,則應調用基類的構造函數,以使基礎對象還原其變量。
如果從實現了ISerializable的類派生出一個新的類,則只要新的類中含有任何需要序列化的變量,就必須同時實現構造函數以及GetObjectData方法。以下代碼片段顯示了如何使用上文所示的MyObject類來完成此操作。
[Serializable]publicclassObjectTwo:MyObject
{
publicintnum;
publicObjectTwo():base()
{}
protectedObjectTwo(SerializationInfosi,StreamingContextcontext):base(si,context)
{
num=si.GetInt32("num");
}
publicoverridevoidGetObjectData(SerializationInfosi,StreamingContextcontext)
{
base.GetObjectData(si,context);
si.AddValue("num",num);
}
}
切記要在反序列化構造函數中調用基類,否則,將永遠不會調用基類上的構造函數,并且在反序列化后也無法構建完整的對象。
7序列化過程的規則
在格式化程序上調用Serialize方法時,對象序列化按照以下規則進行:
檢查格式化程序是否有選取器。如果有,檢查選取器是否處理指定類型的對象。如果選取器處理此對象類型,將在選取器上調用ISerializable.GetObjectData。
如果沒有選取器或有卻不處理此類型,將檢查是否使用Serializable屬性對對象進行標記。如果未標記,將會引發SerializationException。
如果對象已被正確標記,將檢查對象是否實現了ISerializable。如果已實現,將在對象上調用GetObjectData。
如果對象未實現Serializable,將使用默認的序列化策略,對所有未標記為NonSerialized的字段都進行序列化。
8版本控制
.NET框架支持版本控制和并排執行,并且,如果類的接口保持一致,所有類均可跨版本工作。由于序列化涉及的是成員變量而非接口,所以,在向要跨版本序列化的類中添加成員變量,或從中刪除變量時,應謹慎行事。特別是對于未實現ISerializable的類更應如此。若當前版本的狀態發生了任何變化(例如添加成員變量、更改變量類型或更改變量名稱),都意味著如果同一類型的現有對象是使用早期版本進行序列化的,則無法成功對它們進行反序列化。
如果對象的狀態需要在不同版本間發生改變,類的作者可以有兩種選擇:
實現ISerializable。這使您可以精確地控制序列化和反序列化過程,在反序列化過程中正確地添加和解釋未來狀態。
使用NonSerialized屬性標記不重要的成員變量。僅當預計類在不同版本間的變化較小時,才可使用這個選項。例如,把一個新變量添加至類的較高版本后,可以將該變量標記為NonSerialized,以確保該類與早期版本保持兼容。
9序列化規則
在設計新類時應考慮序列化。需要考慮的問題有:是否必須跨應用程序域來發送該類的對象?是否要遠程使用此類?用戶將如何使用此類?是否要派生出一個需要序列化的新類。只要有這種可能性,就應將類標記為可序列化。除下列情況以外,最好將所有類都標記為可序列化:
所有的類都永遠也不會跨越應用程序域。如果某個類不要求序列化但需要跨越應用程序域,請從MarshalByRefObject派生此類。
類存儲僅適用于其當前實例的特殊指針。例如,如果某個類包含非受控的內存或文件句柄,請確保將這些字段標記為NonSerialized或根本不序列化此類。
某些數據成員包含敏感信息。在這種情況下,建議實現ISerializable并僅序列化所要求的字段。
參考文獻
[1]潘愛民著.Com原理與應用.清華大學出版社
[2]SimonRobinsonK.Scott等著.C#高級編程.清華大學出版社
[3]DavidJ.KruglinskiScotWingoGeorgeShepherd著.VC++6.0技術內幕.北京希望電子出版社
- 上一篇:紀檢監察干部培訓開班會講話
- 下一篇:縣紀委書記在賭博會議的講話