序列化流以鍵/值對應保存每個元素。預設特性產生程式碼以變數名為鍵儲存值。添加 ISerializable 介面後,必須匹配鍵名與變數順序(順序即類別中定義順序。順便說,重新排列類別變數名或重新命名,都會破壞對已建立檔案的相容性。)
同樣要求 SerializationFormatter 安全許可權。未實作恰當保護,GetObjectData() 可能存在安全漏洞。惡意程式碼可能產生 StreamingContext,用 GetObjectData() 從物件取得值、不斷修改版本取得另一 SerializationInfo、重新組織修改物件。允許惡意開發者存取物件內部狀態、在流中修改、發送修改版本給你。要求 SerializationFormatter 許可權可封閉漏洞,確保只有受信任程式碼恰當存取類別內部狀態(原則 47)。
我早讓 MyType 成為密封 (sealed),強制只能為葉子類別。基類實作 ISerializable 隱式讓所有衍生類別也序列化。實作意味著所有衍生類別必須建立受保護建構函式與反序列化。為支援非密封類,必須在 GetObjectData() 建立 Hook,讓衍生類別添加自身資料到流。編譯器不捕捉此類錯誤,從流讀取衍生類時,因缺建構函式在執行期拋出例外。缺 Hook 的 GetObjectData() 也意味衍生類資料不會儲存到檔案,也無錯誤拋出。建議:在葉類別實作 Serializable。
為衍生類別序列化,基類必須支援序列化。修改 MyType 為可序列化基類:將序列化建構函式改為 protected,建立虛擬方法讓衍生類別重載並儲存資料:
[Serializable]
public class MyType : ISerializable
{
private string _label;
[NonSerialized] private int _value;
private OtherClass _object;
private const int DEFAULT_VALUE = 5;
private int _value2;
protected MyType(SerializationInfo info, StreamingContext cntxt)
{
_label = info.GetString("_label");
_object = (OtherClass)info.GetValue("_object", typeof(OtherClass));
try { _value2 = info.GetInt32("_value2"); }
catch (SerializationException) { _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);
}
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。