序列化流 是以鍵/值對應的方法來保存每一個元素的。默認的特性生成的代碼是以變量名 做為鍵來存儲值。當你添加了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 接口。
返回教程目錄