亚洲欧美精品沙发,日韩在线精品视频,亚洲Av每日更新在线观看,亚洲国产另类一区在线5

<pre id="hdphd"></pre>

  • <div id="hdphd"><small id="hdphd"></small></div>
      學(xué)習(xí)啦 > 論文大全 > 畢業(yè)論文 > 計(jì)算機(jī)論文 > 計(jì)算機(jī)應(yīng)用 >

      NET中對象序列化方法

      時(shí)間: 王穎1 分享
      摘 要 實(shí)現(xiàn)序列化最重要的兩個(gè)原因是:將對象的狀態(tài)保存在存儲(chǔ)媒體中以便以后重新創(chuàng)建出完全相同的副本;按值將對象從一個(gè)應(yīng)用程序域發(fā)送至另一個(gè)應(yīng)用程序域。例如,序列化可用于在 ASP.NET 中保存會(huì)話狀態(tài);將對象復(fù)制到 Windows 窗體的剪貼板中;它還可用于按值將對象從一個(gè)應(yīng)用程序域遠(yuǎn)程傳遞至另一個(gè)應(yīng)用程序域。本文簡要介紹了 Microsoft .NET 中使用的序列化。
      關(guān)鍵詞 .NET;序列化;封送

      1 引言

      序列化是指將對象實(shí)例的狀態(tài)存儲(chǔ)到存儲(chǔ)媒體的過程。在此過程中,先將對象的公共字段和私有字段以及類的名稱(包括類所在的程序集)轉(zhuǎn)換為字節(jié)流,然后再把字節(jié)流寫入數(shù)據(jù)流。在隨后對對象進(jìn)行反序列化時(shí),將創(chuàng)建出與原對象完全相同的副本。
      在面向?qū)ο蟮沫h(huán)境中實(shí)現(xiàn)序列化機(jī)制時(shí),必須在易用性和靈活性之間進(jìn)行一些權(quán)衡。只要對此過程有足夠的控制能力,就可以使該過程在很大程度上自動(dòng)進(jìn)行。例如,簡單的二進(jìn)制序列化不能滿足需要,或者,由于特定原因需要確定類中哪些字段需要序列化。以下各部分將探討 .NET 框架提供的可靠的序列化機(jī)制,并著重介紹如何根據(jù)需要自定義序列化過程。

      2 持久存儲(chǔ)

      我們經(jīng)常需要將對象的字段值保存到磁盤中,并在以后在內(nèi)存中還原次對象。盡管不使用序列化也能完成這項(xiàng)工作,但這種方法通常很繁瑣而且容易出錯(cuò),并且在需要跟蹤對象的層次結(jié)構(gòu)時(shí),會(huì)變得越來越復(fù)雜??梢韵胂笠幌戮帉懓罅繉ο蟮拇笮蜆I(yè)務(wù)應(yīng)用程序的情形,程序員不得不為每一個(gè)對象編寫代碼,以便將字段和屬性保存至磁盤以及從磁盤還原這些字段和屬性。序列化提供了輕松實(shí)現(xiàn)這個(gè)目標(biāo)的快捷方法。
      公共語言運(yùn)行時(shí) (CLR) 管理對象在內(nèi)存中的分布,.NET 框架則通過使用反射提供自動(dòng)的序列化機(jī)制。對象序列化后,類的名稱、程序集以及類實(shí)例的所有數(shù)據(jù)成員均被寫入存儲(chǔ)媒體中。對象通常用成員變量來存儲(chǔ)對其它實(shí)例的引用。類序列化后,序列化引擎將跟蹤所有已序列化的引用對象,以確保同一對象不被序列化多次。.NET 框架所提供的序列化體系結(jié)構(gòu)可以自動(dòng)正確處理對象圖表和循環(huán)引用。對對象圖表的惟一要求是,由正在進(jìn)行序列化的對象所引用的所有對象都必須標(biāo)記為 Serializable。否則,當(dāng)序列化程序試圖序列化未標(biāo)記的對象時(shí)將會(huì)出現(xiàn)異常。當(dāng)反序列化已序列化的類時(shí),將重新創(chuàng)建該類的對象,并自動(dòng)還原所有數(shù)據(jù)成員的值。

      3 按值封送

      按值封送是指將對象序列化為字節(jié)流,并從一個(gè)應(yīng)用程序域傳輸至另一個(gè)應(yīng)用程序域,然后進(jìn)行反序列化,從而在第二個(gè)應(yīng)用程序域中產(chǎn)生出該對象的一個(gè)副本,這在COM技術(shù)中經(jīng)常提到。在.Net中,對象僅在創(chuàng)建對象的應(yīng)用程序域中有效,除非對象是從 MarshalByRefObject 派生得到或標(biāo)記為 Serializable,否則,任何將對象作為參數(shù)傳遞或作為結(jié)果返回到另外一個(gè)應(yīng)用程序域都將失敗。
      如果對象標(biāo)記為 Serializable,則該對象將被自動(dòng)序列化,并從一個(gè)應(yīng)用程序域傳輸至另一個(gè)應(yīng)用程序域,然后進(jìn)行反序列化,從而在第二個(gè)應(yīng)用程序域中產(chǎn)生出該對象的一個(gè)精確副本。
      如果對象是從 MarshalByRefObject 派生得到,則從一個(gè)應(yīng)用程序域傳遞至另一個(gè)應(yīng)用程序域的是對象引用,而不是對象本身。也可以將從 MarshalByRefObject 派生得到的對象標(biāo)記為 Serializable。遠(yuǎn)程使用此對象時(shí),負(fù)責(zé)進(jìn)行序列化并已預(yù)先配置為 SurrogateSelector 的格式化程序?qū)⒖刂菩蛄谢^程,并用一個(gè)代理替換所有從 MarshalByRefObject 派生得到的對象。如果沒有預(yù)先配置為 SurrogateSelector,序列化體系結(jié)構(gòu)將遵從下面的標(biāo)準(zhǔn)序列化規(guī)則。

      4 基本序列化

      要使一個(gè)類可序列化,最簡單的方法是使用 Serializable 屬性對它進(jìn)行標(biāo)記,如下所示:
      [Serializable]
      public class MyObject
      {
      public int n1 = 0;
      public int n2 = 0;
      public String str = null;
      }
      以下代碼片段說明了如何將此類的一個(gè)實(shí)例序列化為一個(gè)文件:
      MyObject obj = new MyObject();
      obj.n1 = 1;
      obj.n2 = 24;
      obj.str = "一些字符串";
      IFormatter formatter = new BinaryFormatter();
      Stream stream = new FileStream("MyFile.bin",F(xiàn)ileMode.Create,F(xiàn)ileAccess.Write,F(xiàn)ileShare.None);
      formatter.Serialize(stream,obj);
      stream.Close();
      本例使用二進(jìn)制格式化程序進(jìn)行序列化。只需創(chuàng)建一個(gè)要使用的流和格式化程序的實(shí)例,然后調(diào)用格式化程序的 Serialize 方法。流和要序列化的對象實(shí)例作為參數(shù)提供給此調(diào)用。類中包括 private 變量的所有成員變量,都將被序列化,但這一點(diǎn)在本例中未明確體現(xiàn)出來。在這一點(diǎn)上,二進(jìn)制序列化不同于只序列化公共字段的 XML 序列化程序。
      將對象還原到它以前的狀態(tài)也非常容易。首先,創(chuàng)建格式化程序和流以進(jìn)行讀取,然后讓格式化程序?qū)ο筮M(jìn)行反序列化。以下代碼片段說明了如何進(jìn)行此操作。
      IFormatter formatter = new BinaryFormatter();
      Stream stream = new FileStream("MyFile.bin",F(xiàn)ileMode.Open,F(xiàn)ileAccess.Read,F(xiàn)ileShare.Read);
      MyObject obj = (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 效率很高,能生成非常緊湊的字節(jié)流。所有使用此格式化程序序列化的對象也可使用它進(jìn)行反序列化,對于序列化將在 .NET 平臺(tái)上進(jìn)行反序列化的對象,此格式化程序是一個(gè)理想的工具。需要注意的是,對對象進(jìn)行反序列化時(shí)并不調(diào)用構(gòu)造函數(shù)。對反序列化添加這項(xiàng)約束,是出于性能方面的考慮。但是,這違反了對象編寫者通常采用的一些運(yùn)行時(shí)約定,因此,開發(fā)人員在將對象標(biāo)記為可序列化時(shí),應(yīng)確??紤]了這一特殊約定。
      如果要求具有可移植性,應(yīng)該使用 SoapFormatter。所要做的更改只是將以上代碼中的格式化程序換成 SoapFormatter,而 Serialize 和 Deserialize 調(diào)用不變。對于上面使用的示例,該格式化程序?qū)⑸梢韵陆Y(jié)果。
      <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:encoding3">5 選擇性序列化
      類通常包含不應(yīng)被序列化的字段。例如,假設(shè)某個(gè)類用一個(gè)成員變量來存儲(chǔ)線程 ID。當(dāng)此類被反序列化時(shí),序列化此類時(shí)所存儲(chǔ)的 ID 對應(yīng)的線程可能不再運(yùn)行,所以對這個(gè)值進(jìn)行序列化沒有意義??梢酝ㄟ^使用 NonSerialized 屬性標(biāo)記成員變量來防止它們被序列化,如下所示:
      [Serializable]
      public class MyObject
      {
      public int n1;
      [NonSerialized]public int n2;
      public String str;
      }

      6 自定義序列化

      可以通過在對象上實(shí)現(xiàn) ISerializable 接口來自定義序列化過程。這一功能在反序列化后成員變量的值失效時(shí)尤其有用,但是需要為變量提供值以重建對象的完整狀態(tài)。要實(shí)現(xiàn) ISerializable,需要實(shí)現(xiàn) GetObjectData 方法以及一個(gè)特殊的構(gòu)造函數(shù),在反序列化對象時(shí)要用到此構(gòu)造函數(shù)。以下代碼示例說明了如何在前一部分中提到的 MyObject 類上實(shí)現(xiàn) ISerializable接口。
      [Serializable]
      public class MyObject:ISerializable
      {
      public int n1;
      public int n2;
      public String str;
      public MyObject() { }
      protected MyObject(SerializationInfo info,StreamingContext context)
      {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
      }
      public virtual void GetObjectData(SerializationInfo info,StreamingContext context)
      {
      info.AddValue("i",n1);
      info.AddValue("j",n2);
      info.AddValue("k",str);
      }
      }
      在序列化過程中調(diào)用 GetObjectData 時(shí),需要填充方法調(diào)用中提供的 SerializationInfo 對象。只需按名稱/值對的形式添加將要序列化的變量。其名稱可以是任何文本。只要已序列化的數(shù)據(jù)足以在反序列化過程中還原對象,便可以自由選擇添加至 SerializationInfo 的成員變量。如果基對象實(shí)現(xiàn)了 ISerializable,則派生類應(yīng)調(diào)用其基對象的 GetObjectData 方法。
      需要強(qiáng)調(diào)的是,將 ISerializable 添加至某個(gè)類時(shí),需要同時(shí)實(shí)現(xiàn) GetObjectData 以及特殊的構(gòu)造函數(shù)。如果缺少 GetObjectData,編譯器將發(fā)出警告。但是,由于無法強(qiáng)制實(shí)現(xiàn)構(gòu)造函數(shù),所以,缺少構(gòu)造函數(shù)時(shí)不會(huì)發(fā)出警告。如果在沒有構(gòu)造函數(shù)的情況下嘗試反序列化某個(gè)類,將會(huì)出現(xiàn)異常。
      在反序列化過程中,使用出于此目的而提供的構(gòu)造函數(shù)將 SerializationInfo 傳遞給類。對象反序列化時(shí),對構(gòu)造函數(shù)的任何可見性約束都將被忽略,因此,可以將類標(biāo)記為 public、protected、internal 或 private。通常,在類未封裝的情況下,將構(gòu)造函數(shù)標(biāo)記為 protect。如果類已封裝,則應(yīng)標(biāo)記為 private。要還原對象的狀態(tài),只需使用序列化時(shí)采用的名稱,從 SerializationInfo 中檢索變量的值。如果基類實(shí)現(xiàn)了 ISerializable,則應(yīng)調(diào)用基類的構(gòu)造函數(shù),以使基礎(chǔ)對象還原其變量。
      如果從實(shí)現(xiàn)了 ISerializable 的類派生出一個(gè)新的類,則只要新的類中含有任何需要序列化的變量,就必須同時(shí)實(shí)現(xiàn)構(gòu)造函數(shù)以及 GetObjectData 方法。以下代碼片段顯示了如何使用上文所示的 MyObject 類來完成此操作。
      [Serializable]public class ObjectTwo:MyObject
      {
      public int num;
      public ObjectTwo():base()
      { }
      protected ObjectTwo(SerializationInfo si,StreamingContext context):base(si,context)
      {
      num = si.GetInt32("num");
      }
      public override void GetObjectData(SerializationInfo si,StreamingContext context)
      {
      base.GetObjectData(si,context);
      si.AddValue("num",num);
      }
      }
      切記要在反序列化構(gòu)造函數(shù)中調(diào)用基類,否則,將永遠(yuǎn)不會(huì)調(diào)用基類上的構(gòu)造函數(shù),并且在反序列化后也無法構(gòu)建完整的對象。

      7 序列化過程的規(guī)則

      在格式化程序上調(diào)用 Serialize 方法時(shí),對象序列化按照以下規(guī)則進(jìn)行:
      檢查格式化程序是否有代理選取器。如果有,檢查代理選取器是否處理指定類型的對象。如果選取器處理此對象類型,將在代理選取器上調(diào)用 ISerializable.GetObjectData。
      如果沒有代理選取器或有卻不處理此類型,將檢查是否使用 Serializable 屬性對對象進(jìn)行標(biāo)記。如果未標(biāo)記,將會(huì)引發(fā) SerializationException。
      如果對象已被正確標(biāo)記,將檢查對象是否實(shí)現(xiàn)了 ISerializable。如果已實(shí)現(xiàn),將在對象上調(diào)用 GetObjectData。
      如果對象未實(shí)現(xiàn) Serializable,將使用默認(rèn)的序列化策略,對所有未標(biāo)記為 NonSerialized 的字段都進(jìn)行序列化。

      8 版本控制

      .NET 框架支持版本控制和并排執(zhí)行,并且,如果類的接口保持一致,所有類均可跨版本工作。由于序列化涉及的是成員變量而非接口,所以,在向要跨版本序列化的類中添加成員變量,或從中刪除變量時(shí),應(yīng)謹(jǐn)慎行事。特別是對于未實(shí)現(xiàn) ISerializable 的類更應(yīng)如此。若當(dāng)前版本的狀態(tài)發(fā)生了任何變化(例如添加成員變量、更改變量類型或更改變量名稱),都意味著如果同一類型的現(xiàn)有對象是使用早期版本進(jìn)行序列化的,則無法成功對它們進(jìn)行反序列化。
      如果對象的狀態(tài)需要在不同版本間發(fā)生改變,類的作者可以有兩種選擇:
      實(shí)現(xiàn) ISerializable。這使您可以精確地控制序列化和反序列化過程,在反序列化過程中正確地添加和解釋未來狀態(tài)。
      使用 NonSerialized 屬性標(biāo)記不重要的成員變量。僅當(dāng)預(yù)計(jì)類在不同版本間的變化較小時(shí),才可使用這個(gè)選項(xiàng)。例如,把一個(gè)新變量添加至類的較高版本后,可以將該變量標(biāo)記為 NonSerialized,以確保該類與早期版本保持兼容。

      9 序列化規(guī)則

      在設(shè)計(jì)新類時(shí)應(yīng)考慮序列化。需要考慮的問題有:是否必須跨應(yīng)用程序域來發(fā)送該類的對象?是否要遠(yuǎn)程使用此類?用戶將如何使用此類?是否要派生出一個(gè)需要序列化的新類。只要有這種可能性,就應(yīng)將類標(biāo)記為可序列化。除下列情況以外,最好將所有類都標(biāo)記為可序列化:
      所有的類都永遠(yuǎn)也不會(huì)跨越應(yīng)用程序域。如果某個(gè)類不要求序列化但需要跨越應(yīng)用程序域,請從 MarshalByRefObject 派生此類。
      類存儲(chǔ)僅適用于其當(dāng)前實(shí)例的特殊指針。例如,如果某個(gè)類包含非受控的內(nèi)存或文件句柄,請確保將這些字段標(biāo)記為 NonSerialized 或根本不序列化此類。
      某些數(shù)據(jù)成員包含敏感信息。在這種情況下,建議實(shí)現(xiàn) ISerializable 并僅序列化所要求的字段。

      參考文獻(xiàn)

      [1] 潘愛民著.Com原理與應(yīng)用.清華大學(xué)出版社
      [2] Simon Robinson K.Scott等著.C#高級編程. 清華大學(xué)出版社
      [3] David J. Kruglinski Scot Wingo George Shepherd 著.VC++6.0技術(shù)內(nèi)幕. 北京希望電子出版社
      3157