自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

.NET Framework序列化相關(guān)應(yīng)用概念詳解

開發(fā) 后端
.NET Framework序列化能夠?qū)崿F(xiàn)持久存儲;按值封送等。我們可以通過這篇文章介紹的相關(guān)概念詳細解讀其中的各種應(yīng)用實現(xiàn)技巧。

.NET Framework已經(jīng)推出就深受廣大開發(fā)人員青睞。因為其具有非常強大的兼容性,使得.NET Framework的發(fā)展穩(wěn)步向前。.NET Framework序列化是將對象狀態(tài)轉(zhuǎn)換為可保持或傳輸?shù)母袷降倪^程。與序列化相對的是反序列化,它將流轉(zhuǎn)換為對象。這兩個過程結(jié)合起來,可以輕松地存儲和傳輸數(shù)據(jù)。#t#

.NET Framework 提供兩種.NET Framework序列化技術(shù):

二進制序列化保持類型保真度,這對于在應(yīng)用程序的不同調(diào)用之間保留對象的狀態(tài)很有用。例如,通過將對象序列化到剪貼板,可在不同的應(yīng)用程序之間共享對象。您可以將對象序列化到流、磁盤、內(nèi)存和網(wǎng)絡(luò)等等。遠程處理使用序列化“通過值”在計算機或應(yīng)用程序域之間傳遞對象。

XML 序列化僅序列化公共屬性和字段,且不保持類型保真度。當您要提供或使用數(shù)據(jù)而不限制使用該數(shù)據(jù)的應(yīng)用程序時,這一點是很有用的。由于 XML 是一個開放式標準,因此,對于通過 Web 共享數(shù)據(jù)而言,這是一個很好的選擇。SOAP 同樣是一個開放式標準,這使它也成為一個頗具吸引力的選擇。

簡介

.NET Framework序列化是指將對象實例的狀態(tài)存儲到存儲媒體的過程。在此過程中,先將對象的公共字段和私有字段以及類的名稱(包括類所在的程序集)轉(zhuǎn)換為字節(jié)流,然后再把字節(jié)流寫入數(shù)據(jù)流。在隨后對對象進行反序列化時,將創(chuàng)建出與原對象完全相同的副本。

在面向?qū)ο蟮沫h(huán)境中實現(xiàn)序列化機制時,必須在易用性和靈活性之間進行一些權(quán)衡。只要您對此過程有足夠的控制能力,就可以使該過程在很大程度上自動進行。例如,簡單的二進制序列化不能滿足需要,或者,由于特定原因需要確定類中那些字段需要序列化。以下各部分將探討 .NET 框架提供的可靠的序列化機制,并著重介紹使您可以根據(jù)需要自定義序列化過程的一些重要功能。

持久存儲

我們經(jīng)常需要將對象的字段值保存到磁盤中,并在以后檢索此數(shù)據(jù)。盡管不使用序列化也能完成這項工作,但這種方法通常很繁瑣而且容易出錯,并且在需要跟蹤對象的層次結(jié)構(gòu)時,會變得越來越復(fù)雜。可以想象一下編寫包含大量對象的大型業(yè)務(wù)應(yīng)用程序的情形,程序員不得不為每一個對象編寫代碼,以便將字段和屬性保存至磁盤以及從磁盤還原這些字段和屬性。序列化提供了輕松實現(xiàn)這個目標的快捷方法。

公共語言運行時 (CLR) 管理對象在內(nèi)存中的分布,.NET 框架則通過使用反射提供自動的序列化機制。對象序列化后,類的名稱、程序集以及類實例的所有數(shù)據(jù)成員均被寫入存儲媒體中。對象通常用成員變量來存儲對其他實例的引用。類序列化后,序列化引擎將跟蹤所有已序列化的引用對象,以確保同一對象不被序列化多次。.NET 框架所提供的序列化體系結(jié)構(gòu)可以自動正確處理對象圖表和循環(huán)引用。對對象圖表的唯一要求是,由正在進行序列化的對象所引用的所有對象都必須標記為 Serializable(請參閱基本序列化)。否則,當序列化程序試圖序列化未標記的對象時將會出現(xiàn)異常。

當反序列化已序列化的類時,將重新創(chuàng)建該類,并自動還原所有數(shù)據(jù)成員的值。

按值封送

對象僅在創(chuàng)建對象的應(yīng)用程序域中有效。除非對象是從 MarshalByRefObject 派生得到或標記為 Serializable,否則,任何將對象作為參數(shù)傳遞或?qū)⑵渥鳛榻Y(jié)果返回的嘗試都將失敗。如果對象標記為 Serializable,則該對象將被自動序列化,并從一個應(yīng)用程序域傳輸至另一個應(yīng)用程序域,然后進行反序列化,從而在第二個應(yīng)用程序域中產(chǎn)生出該對象的一個精確副本。此過程通常稱為按值封送。

如果對象是從 MarshalByRefObject 派生得到,則從一個應(yīng)用程序域傳遞至另一個應(yīng)用程序域的是對象引用,而不是對象本身。也可以將從 MarshalByRefObject 派生得到的對象標記為 Serializable。遠程使用此對象時,負責進行序列化并已預(yù)先配置為 SurrogateSelector 的格式化程序?qū)⒖刂菩蛄谢^程,并用一個代理替換所有從 MarshalByRefObject 派生得到的對象。如果沒有預(yù)先配置為 SurrogateSelector,序列化體系結(jié)構(gòu)將遵從下面的標準序列化規(guī)則(請參閱序列化過程的步驟)。

基本.NET Framework序列化

要使一個類可序列化,最簡單的方法是使用 Serializable 屬性對它進行標記,如下所示:

  1. [Serializable]public class MyObject   
  2. {   
  3. public int n1 = 0;   
  4. public int n2 = 0;   
  5. public String str = null;  

以下代碼片段說明了如何將此類的一個實例.NET Framework序列化為一個文件:

  1. MyObject obj = new MyObject();  
  2. obj.n1 = 1;obj.n2 = 24;obj.str = "一些字符串";  
  3. IFormatter formatter = new BinaryFormatter();  
  4. Stream stream = new FileStream
    ("MyFile.bin", 
    FileMode.Create, FileAccess.Write, 
    FileShare.None);  
  5. formatter.Serialize(stream, obj);
  6. stream.Close(); 

本例使用二進制格式化程序進行序列化。您只需創(chuàng)建一個要使用的流和格式化程序的實例,然后調(diào)用格式化程序的 Serialize 方法。流和要序列化的對象實例作為參數(shù)提供給此調(diào)用。類中的所有成員變量(甚至標記為 private 的變量)都將被序列化,但這一點在本例中未明確體現(xiàn)出來。在這一點上,二進制序列化不同于只序列化公共字段的 XML 序列化程序。

將對象還原到它以前的狀態(tài)也非常容易。首先,創(chuàng)建格式化程序和流以進行讀取,然后讓格式化程序?qū)ο筮M行反序列化。以下代碼片段說明了如何進行此操作。

  1. IFormatter formatter = new BinaryFormatter();  
  2. Stream stream = new FileStream
    ("MyFile.bin", FileMode.Open, FileAccess.
    Read, FileShare.Read);  
  3. MyObject obj = (MyObject) formatter.
    Deserialize(fromStream);  
  4. stream.Close();  
  5. // 下面是證明  
  6. Console.WriteLine("n1: {0}", obj.n1);  
  7. Console.WriteLine("n2: {0}", obj.n2);  
  8. Console.WriteLine("str: {0}", obj.str); 

上面所使用的 BinaryFormatter 效率很高,能生成非常緊湊的字節(jié)流。所有使用此格式化程序序列化的對象也可使用它進行反序列化,對于.NET Framework序列化將在 .NET 平臺上進行反序列化的對象,此格式化程序無疑是一個理想工具。需要注意的是,對對象進行反序列化時并不調(diào)用構(gòu)造函數(shù)。對反序列化添加這項約束,是出于性能方面的考慮。但是,這違反了對象編寫者通常采用的一些運行時約定,因此,開發(fā)人員在將對象標記為可序列化時,應(yīng)確保考慮了這一特殊約定。

如果要求具有可移植性,請使用 SoapFormatter。所要做的更改只是將以上代碼中的格式化程序換成 SoapFormatter,而 Serialize 和 Deserialize 調(diào)用不變。對于上面使用的示例,該格式化程序?qū)⑸梢韵陆Y(jié)果。

  1. < SOAP-ENV:Envelope xmlns:xsi=http://www.w3.
    org/2001/XMLSchema-instance 
    xmlns:xsd="http:
    //www.w3.org/2001/XMLSchema"
     
    xmlns:SOAP- 
    ENC=http://schemas.xmlsoap.org/soap/encoding/ 
    xmlns:SOAP- 
    ENV=http://schemas.xmlsoap.org/
    soap/envelope/ 
    SOAP-ENV:encodingStyle"
    http://schemas.microsoft.com/soap/encoding/
    clr/1.0 http://schemas.xmlsoap.org/soap/encoding/"
     
    xmlns:a1="http://schemas.microsoft.com/clr/
    assem/ToFile"
    > < SOAP-ENV:Body> 
  2. < a1:MyObject id="ref-1"> 
  3. < n1>1< /n1> < n2>24< /n2> 
  4. < str id="ref-3">一些字符串< /str> 
  5. < /a1:MyObject> < /SOAP-ENV:Body>
  6. < /SOAP-ENV:Envelope> 

需要注意的是,無法繼承 Serializable 屬性。如果從 MyObject 派生出一個新的類,則這個新的類也必須使用該屬性進行標記,否則將無法序列化。例如,如果試圖序列化以下類實例,將會顯示一個 SerializationException,說明 MyStuff 類型未標記為可序列化。

public class MyStuff : MyObject { public int n3;}使用序列化屬性非常方便,但是它存在上述的一些限制。有關(guān)何時標記類以進行序列化(因為類編譯后就無法再序列化),請參考有關(guān)說明(請參閱下面的序列化規(guī)則)。

選擇性.NET Framework序列化

類通常包含不應(yīng)被序列化的字段。例如,假設(shè)某個類用一個成員變量來存儲線程 ID。當此類被反序列化時,序列化此類時所存儲的 ID 對應(yīng)的線程可能不再運行,所以對這個值進行序列化沒有意義??梢酝ㄟ^使用 NonSerialized 屬性標記成員變量來防止它們被序列化,如下所示:

[Serializable]public class MyObject { public int n1; [NonSerialized] public int n2; public String str;}自定義序列化
可以通過在對象上實現(xiàn) ISerializable 接口來自定義序列化過程。這一功能在反序列化后成員變量的值失效時尤其有用,但是需要為變量提供值以重建對象的完整狀態(tài)。要實現(xiàn) ISerializable,需要實現(xiàn) GetObjectData 方法以及一個特殊的構(gòu)造函數(shù),在反序列化對象時要用到此構(gòu)造函數(shù)。以下代碼示例說明了如何在前一部分中提到的 MyObject 類上實現(xiàn) ISerializable。

  1. [Serializable]public class MyObject 
    : ISerializable   
  2. {   
  3. public int n1;   
  4. public int n2;   
  5. public String str;   
  6. public MyObject()   
  7. {   
  8. }   
  9. protected MyObject(SerializationInfo 
    info, StreamingContext context)   
  10. {   
  11. n1 = info.GetInt32("i");   
  12. n2 = info.GetInt32("j");   
  13. str = info.GetString("k");   
  14. }   
  15. public virtual void GetObjectData
    (SerializationInfo info, Streaming
    Context context)   
  16. {   
  17. info.AddValue("i", n1);   
  18. info.AddValue("j", n2);   
  19. info.AddValue("k", str);   
  20. }  

在.NET Framework序列化過程中調(diào)用 GetObjectData 時,需要填充方法調(diào)用中提供的 SerializationInfo 對象。只需按名稱/值對的形式添加將要序列化的變量。其名稱可以是任何文本。只要已序列化的數(shù)據(jù)足以在反序列化過程中還原對象,便可以自由選擇添加至 SerializationInfo 的成員變量。如果基對象實現(xiàn)了 ISerializable,則派生類應(yīng)調(diào)用其基對象的 GetObjectData 方法。

需要強調(diào)的是,將 ISerializable 添加至某個類時,需要同時實現(xiàn) GetObjectData 以及特殊的構(gòu)造函數(shù)。如果缺少 GetObjectData,編譯器將發(fā)出警告。但是,由于無法強制實現(xiàn)構(gòu)造函數(shù),所以,缺少構(gòu)造函數(shù)時不會發(fā)出警告。如果在沒有構(gòu)造函數(shù)的情況下嘗試反序列化某個類,將會出現(xiàn)異常。在消除潛在安全性和版本控制問題等方面,當前設(shè)計優(yōu)于 SetObjectData 方法。例如,如果將 SetObjectData 方法定義為某個接口的一部分,則此方法必須是公共方法,這使得用戶不得不編寫代碼來防止多次調(diào)用 SetObjectData 方法??梢韵胂螅绻硞€對象正在執(zhí)行某些操作,而某個惡意應(yīng)用程序卻調(diào)用此對象的 SetObjectData 方法,將會引起一些潛在的麻煩。

在反序列化過程中,使用出于此目的而提供的構(gòu)造函數(shù)將 SerializationInfo 傳遞給類。對象反序列化時,對構(gòu)造函數(shù)的任何可見性約束都將被忽略,因此,可以將類標記為 public、protected、internal 或 private。一個不錯的辦法是,在類未封裝的情況下,將構(gòu)造函數(shù)標記為 protect。如果類已封裝,則應(yīng)標記為 private。要還原對象的狀態(tài),只需使用序列化時采用的名稱,從 SerializationInfo 中檢索變量的值。如果基類實現(xiàn)了 ISerializable,則應(yīng)調(diào)用基類的構(gòu)造函數(shù),以使基礎(chǔ)對象可以還原其變量。

#p#

如果從實現(xiàn)了 ISerializable 的類派生出一個新的類,則只要新的類中含有任何需要序列化的變量,就必須同時實現(xiàn)構(gòu)造函數(shù)以及 GetObjectData 方法。以下代碼片段顯示了如何使用上文所示的 MyObject 類來完成此操作。

  1. [Serializable]  
  2. public class ObjectTwo : MyObject
    { public int num;  
  3. public ObjectTwo() : base()   
  4. {   
  5. }   
  6. protected ObjectTwo(SerializationInfo 
    si,StreamingContext context) : 
    base(si,context)   
  7. {   
  8. num = si.GetInt32("num");   
  9. }   
  10. public override void GetObjectData
    (SerializationInfo si, Streaming
    Context context)   
  11. {   
  12. base.GetObjectData(si,context);   
  13. si.AddValue("num", num);   
  14. }  

切記要在反序列化構(gòu)造函數(shù)中調(diào)用基類,否則,將永遠不會調(diào)用基類上的構(gòu)造函數(shù),并且在反序列化后也無法構(gòu)建完整的對象。

對象被徹底重新構(gòu)建,但是在反系列化過程中調(diào)用方法可能會帶來不良的副作用,因為被調(diào)用的方法可能引用了在調(diào)用時尚未反序列化的對象引用。如果正在進行反序列化的類實現(xiàn)了 IDeserializationCallback,則反序列化整個對象圖表后,將自動調(diào)用 OnSerialization 方法。此時,引用的所有子對象均已完全還原。有些類不使用上述事件偵聽器,很難對它們進行反序列化,散列表便是一個典型的例子。在反序列化過程中檢索關(guān)鍵字/值對非常容易,但是,由于無法保證從散列表派生出的類已反序列化,所以把這些對象添加回散列表時會出現(xiàn)一些問題。因此,建議目前不要在散列表上調(diào)用方法。

.NET Framework序列化過程的步驟

在格式化程序上調(diào)用 Serialize 方法時,對象序列化按照以下規(guī)則進行:

檢查格式化程序是否有代理選取器。如果有,檢查代理選取器是否處理指定類型的對象。如果選取器處理此對象類型,將在代理選取器上調(diào)用 ISerializable.GetObjectData。
如果沒有代理選取器或有卻不處理此類型,將檢查是否使用 Serializable 屬性對對象進行標記。如果未標記,將會引發(fā) SerializationException。
如果對象已被正確標記,將檢查對象是否實現(xiàn)了 ISerializable。如果已實現(xiàn),將在對象上調(diào)用 GetObjectData。
如果對象未實現(xiàn) Serializable,將使用默認的序列化策略,對所有未標記為 NonSerialized 的字段都進行序列化。

版本控制

.NET 框架支持版本控制和并排執(zhí)行,并且,如果類的接口保持一致,所有類均可跨版本工作。由于序列化涉及的是成員變量而非接口,所以,在向要跨版本序列化的類中添加成員變量,或從中刪除變量時,應(yīng)謹慎行事。特別是對于未實現(xiàn) ISerializable 的類更應(yīng)如此。若當前版本的狀態(tài)發(fā)生了任何變化(例如添加成員變量、更改變量類型或更改變量名稱),都意味著如果同一類型的現(xiàn)有對象是使用早期版本進行序列化的,則無法成功對它們進行反序列化。

如果對象的狀態(tài)需要在不同版本間發(fā)生改變,類的作者可以有兩種選擇:

實現(xiàn) ISerializable。這使您可以精確地控制序列化和反序列化過程,在反序列化過程中正確地添加和解釋未來狀態(tài)。

使用 NonSerialized 屬性標記不重要的成員變量。僅當預(yù)計類在不同版本間的變化較小時,才可使用這個選項。例如,把一個新變量添加至類的較高版本后,可以將該變量標記為 NonSerialized,以確保該類與早期版本保持兼容。

.NET Framework序列化規(guī)則

由于類編譯后便無法序列化,所以在設(shè)計新類時應(yīng)考慮序列化。需要考慮的問題有:是否必須跨應(yīng)用程序域來發(fā)送此類?是否要遠程使用此類?用戶將如何使用此類?也許他們會從我的類中派生出一個需要序列化的新類。只要有這種可能性,就應(yīng)將類標記為可序列化。除下列情況以外,最好將所有類都標記為可序列化:

所有的類都永遠也不會跨越應(yīng)用程序域。如果某個類不要求序列化但需要跨越應(yīng)用程序域,請從 MarshalByRefObject 派生此類。
類存儲僅適用于其當前實例的特殊指針。例如,如果某個類包含非受控的內(nèi)存或文件句柄,請確保將這些字段標記為 NonSerialized 或根本不序列化此類。
某些數(shù)據(jù)成員包含敏感信息。在這種情況下,建議實現(xiàn) ISerializable 并僅序列化所要求的字段。
示例:將一個對象序列化--存儲--反序列化

 

  1. UserInfo user = new UserInfo();  
  2. user.UserName = "王國富";  
  3. XmlSerializer serializer = 
    new XmlSerializer(typeof(UserInfo));  
  4. //序列化對象  
  5. MemoryStream str = new MemoryStream();  
  6. serializer.Serialize(str, user);  
  7. byte[] by = str.ToArray();  
  8. string xml = System.Text.
    Encoding.UTF8.GetString(by);  
  9. //序列化后存儲  
  10. //反序列化  
  11. byte[] by2 = System.Text.
    Encoding.UTF8.GetBytes(xml);  
  12. MemoryStream str2 = new 
    MemoryStream(by2);  
  13. str2.Position = 0;  
  14. XmlSerializer serializer2 = 
    new XmlSerializer(typeof(UserInfo));  
  15. UserInfo user2 = (UserInfo)
    serializer2.Deserialize(str2);  
  16. Response.Write(user2.UserName); 

.NET Framework序列化相關(guān)概念就為大家介紹到這里。

責任編輯:曹凱 來源: 博客園
相關(guān)推薦

2010-01-05 16:15:05

.NET Framew

2010-01-05 10:29:43

.NET Framew

2010-01-06 19:22:43

.NET Framew

2009-09-09 14:45:41

XML序列化和反序列化

2010-01-05 14:04:53

.NET Framew

2010-01-06 18:21:24

2009-09-09 15:47:27

XML序列化和反序列化

2010-01-05 18:09:07

.NET Framew

2009-08-06 11:16:25

C#序列化和反序列化

2023-11-13 16:33:46

2009-09-09 16:10:11

.NET序列化和反序列

2010-01-05 14:45:58

.NET Framew

2011-06-01 14:26:11

序列化

2009-07-29 13:39:02

JSON序列化和反序列ASP.NET AJA

2009-12-21 16:52:02

WCF序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2016-12-20 14:55:52

JavaScript鏈式結(jié)構(gòu)序列

2009-08-19 10:13:22

Remoting序列化

2016-09-21 00:15:27

2024-05-06 00:00:00

C#序列化技術(shù)
點贊
收藏

51CTO技術(shù)棧公眾號