程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Effective C#原則25 讓你的類型支持序列化(3)

Effective C#原則25 讓你的類型支持序列化(3)

編輯:關於C語言

序列化流 是以鍵/值對應的方法來保存每一個元素的。默認的特性生成的代碼是以變量名 做為鍵來存儲值。當你添加了ISerializable接口後,你必須匹配鍵名以及變量 順序。這個順序就是在類中定義時的順序。(順便說一句,這實際上就是說重新 排列類中的變量名或者重新給變量命名,都會破壞對已經創建了的文件的兼容性 。)

同樣,我已經要求過SerializationFormatter的安全許可。如果不實 行恰當的保護,對於你的類來說,GetObjectData()可能存在安全漏洞。惡意代 碼可能會產生一個StreamingContext,從而可以用GetObjectData()方法從對象 中取得值,或者不斷修改版本而取得另一個SerializationInfo,或者重新組織 修改的對象。這就許可了惡意的開發者來訪問對象的內部狀態,在流中修改它們 ,然而發送一個修改後的版本給你。對SerializationFormatter進行許可要求可 以封閉這個安全漏洞。這樣可以確保只有受信任的代碼才能恰當的訪問類的內部 狀態(參見原則47)。

但在使用ISerializable接口時有一個弊端,你可以 看到,我很早就讓MyType成為密封(sealed)的,這就強制讓它只能成為葉子類 (leaf class)。在基類實現ISerializable接口就隱式的讓所有派生類也序列化 。實現ISerializable就意味關所有派生類必須創建受保護構造函數以及反序列 化。另外,為了支持非密封類,你必須在GetObjectData()方法創建hook,從而 讓派生類可以添加它們自己的數據到流中。編譯器不會捕獲任何這樣的錯誤,當 從流中讀取派生類時,因缺少恰當的構造構造函數會在運行時拋出異常。缺少 hook的GetObjectData()方法也意味著從派生類來的數據不會保存到文件中。當 然也不會有錯誤拋出。所以我要推薦:在葉類中實現Serializable。

我 沒有說這,因為它不工作:為了派生類的序列化,你的基類必須支持序列化。修 改MyType ,讓它成為了一個可序列化的基類,你要把序列化構造函數修改為 protected,然後創建一個虛方法,這樣派生類就可以重載它並存儲它們的數據 。

using System.Runtime.Serialization;
using System.Security.Permissions;
[Serializable]
public class MyType : ISerializable
{
 private string _label;
  [NonSerialized]
 private int _value;
 private OtherClass  _object;
 private const int DEFAULT_VALUE = 5;
 private int _value2;
 // public constructors elided.
 // Protected constructor used only by the Serialization
    framework.
 protected MyType( SerializationInfo info,
   StreamingContext cntxt )
 {
  _label = info.GetString( "_label" );
  _object = ( OtherClass )info.GetValue( "_object", typeof
   ( OtherClass ));
  try {
   _value2 = info.GetInt32( "_value2" );
   } catch ( SerializationException e )
  {
   // Found version 1.
   _value2 = DEFAULT_VALUE;
  }
 }
 [ SecurityPermissionAttribute( SecurityAction.Demand,
   SerializationFormatter =true ) ]
 void ISerializable.GetObjectData(
  SerializationInfo inf,
   StreamingContext cxt )
 {
  inf.AddValue( "_label", _label );
  inf.AddValue( "_object", _object );
  inf.AddValue( "_value2", _value2 );
  WriteObjectData( inf, cxt );
 }
 // Overridden in derived classes to write
 // derived class data:
 protected virtual void
   WriteObjectData(
  SerializationInfo inf,
   StreamingContext cxt )
 {
 }
}

一個派生 類應該提供它自己的序列化構造函數,並且重載WriteObjectData方法:

public class DerivedType : MyType
{
 private int _DerivedVal;
 private DerivedType ( SerializationInfo info,
  StreamingContext cntxt ) :
   base( info, cntxt )
 {
   _DerivedVal = info.GetInt32( "_DerivedVal" );
 }
 protected override void WriteObjectData(
  SerializationInfo inf,
   StreamingContext cxt )
 {
  inf.AddValue( "_DerivedVal", _DerivedVal );
 }
}

從流中寫入和讀取值的順序必須保持一致。我相信先讀寫基類的數據應該簡單一 些,所以我就這樣做了。如果你寫的代碼不對整個繼承關系進行精確的順序序列 化,那麼你的序列化代碼是無效的。

.Net框架提供了一個簡單的方法, 也是標准的算法來支持對象的序列化。如果你的類型須要持久,你應該遵守這個 標准的實現。如果你的類型不支持序列化,那化其它使用這個類的類也不能序列 。為了讓使用類的客戶更加方便,盡可能的使用默認序列化特性,並且在默認的 特性不滿足時要實現ISerializable 接口。

返回教程目錄

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved