類別 Example 實作 IDeserializationCallback,並在 OnDeserialization 中遍歷成員字典 map 輸出內容。預期:反序列化後立即輸出字典鍵值對。實際:**OnDeserialization 執行時,map 尚未重建完成,遍歷無輸出。**
Dictionary<TKey, TValue> 內部實作 IDeserializationCallback,其 OnDeserialization 負責從 SerializationInfo 重建雜湊表 (buckets, entries)。當 Example 被反序列化時,物件圖包含兩個實作該介面的物件:Example 本身與 Example.map。.NET 序列化器**不保證調用順序**,實測下 Example.OnDeserialization 先於 Dictionary.OnDeserialization 執行,導致邏輯依賴失效。


放棄依賴 Dictionary 自動序列化,改由外層類別 Example 完整接管:
map 為 [NonSerialized],阻止預設序列化。ISerializable.GetObjectData:將字典鍵值對拆解為兩個陣列 (keys[], vals[]) 寫入 SerializationInfo。Example(SerializationInfo, StreamingContext):從 SerializationInfo 讀取陣列,**立即重建字典** (而非等待回呼)。OnDeserialization 保留為空或僅做後續初始化,此時字典已就緒。[Serializable]
public class Example : ISerializable, IDeserializationCallback {
[NonSerialized] private Dictionary<string, string> _map = new Dictionary<string, string>();
public Example() { _map.Add("one", "1"); _map.Add("two", "2"); }
// 序列化建構函式:反序列化時優先調用,此處主動重建字典
protected Example(SerializationInfo info, StreamingContext context) {
var keys = (string[])info.GetValue("MapKeys", typeof(string[]));
var vals = (string[])info.GetValue("MapVals", typeof(string[]));
_map = new Dictionary<string, string>();
for (int i = 0; i < keys.Length; i++) _map.Add(keys[i], vals[i]);
}
public void GetObjectData(SerializationInfo info, StreamingContext context) {
var keys = new string[_map.Count];
var vals = new string[_map.Count];
int i = 0;
foreach (var kv in _map) { keys[i] = kv.Key; vals[i] = kv.Value; i++; }
info.AddValue("MapKeys", keys);
info.AddValue("MapVals", vals);
}
public void OnDeserialization(object sender) {
// 此時 _map 已在建構函式中重建完成,可安全使用
Dump();
}
// ...
}
若外層類別實作 IDeserializationCallback,且其回呼邏輯依賴某個成員物件,必須檢查該成員是否也實作 IDeserializationCallback。若是,外層類別必須改實作 ISerializable,在序列化建構函式中主動重建該成員,切斷對回呼順序的依賴。
List<T>、HashSet<T>、SortedDictionary<TKey,TValue> 等集合類型 (均實作 IDeserializationCallback)。ISerializable 與 IDeserializationCallback (雙重保險模式) 時的順序衝突。IDeserializationCallback 看似方便的「反序列化後統一初始化」機制,實則隱藏著物件圖拓樸順序的不確定性陷阱。掌握 ISerializable 序列化建構函式的**優先調用特權**,在建構階段主動重建依賴成員,是解決此類時序衝突的根本之道。這也提醒我們:序列化不僅是資料持久化,更是物件圖重建的精密協同過程,需對框架內部機制有清鍾認知。