物件持久化是基本功能。若型別不支援序列化,所有包含該型別為成員或基類的類別都將無法正確序列化,強迫消費者繞道手寫序列化邏輯,甚至因無法存取私有成員而不可能完成。
[Serializable]
public class MyType {
private string _label;
private int _value;
private OtherClass _object; // OtherClass 也必須可序列化
}
所有成員皆為可序列化型別時,僅需標記屬性即可。支援二進位與 SOAP 兩種格式,自動處理物件圖(含循環參考)。
[Serializable]
public class MyType {
private string _label;
[NonSerialized] private int _cachedValue; // 不序列化
private OtherClass _object;
}
反序列化時,NonSerialized 欄位保持預設值(0/null)。若需自訂初始化,實作 IDeserializationCallback.OnDeserialization(物件圖完成後呼叫),但需注意呼叫順序不固定,公開成員必須能處理欄位未初始化狀態。
新增欄位會導致舊版資料反序列化失敗。實作 ISerializable 掌控完整流程:
[Serializable]
public sealed class MyType : ISerializable {
private string _label;
[NonSerialized] private int _value;
private OtherClass _object;
private int _value2; // v2 新增
private const int DEFAULT_VALUE = 5;
// 序列化建構函式(反序列化專用)
private MyType(SerializationInfo info, StreamingContext ctx) {
_label = info.GetString("_label");
_object = (OtherClass)info.GetValue("_object", typeof(OtherClass));
try { _value2 = info.GetInt32("_value2"); }
catch (SerializationException) { _value2 = DEFAULT_VALUE; } // 相容 v1
}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext ctx) {
info.AddValue("_label", _label);
info.AddValue("_object", _object);
info.AddValue("_value2", _value2);
}
}
關鍵點:
SerializationFormatter 安全權限,防惡意代碼竄改資料流。sealed 類別實作,避免衍生類別序列化複雜度。基類建構函式改為 protected,提供 virtual WriteObjectData 讓衍生類別擴充:
protected MyType(SerializationInfo info, StreamingContext ctx) { ... }
void ISerializable.GetObjectData(...) { ...; WriteObjectData(info, ctx); }
protected virtual void WriteObjectData(SerializationInfo info, StreamingContext ctx) { }
衍生類別覆寫 WriteObjectData 並提供自身序列化建構函式呼叫 base(info, ctx)。
預設用 [Serializable]、需要控制用 [NonSerialized]、版本演進/複雜圖形用 ISerializable。讓所有型別支援序列化,是對消費者負責的基本態度。