版本相容序列化
在 .NET Framework 1.0 和 1.1 版中,建立可在各版應用程式間重複使用的可序列化型別是一項困難的工作。如果藉由加入其他欄位來修改型別,則會發生下列問題:
-
被要求還原序列化較新版本的舊型別時,較舊版本的應用程式會擲回例外狀況。
-
還原序列化資料不全的較舊版本型別時,較新版本的應用程式會擲回例外狀況。
版本相容序列化 (VTS) 是 .NET Framework 2.0 中引進的一組功能,可使修改可序列化型別更輕鬆且持久。特別是,套用 SerializableAttribute 屬性的類別會啟用 VTS 功能。VTS 允許將新的欄位加入這些類別,而不破壞與其他版本之型別的相容性。針對工作範例應用程式,請參閱 版本相容序列化技術範例。
使用 BinaryFormatter 時,會啟用 VTS 功能。此外,使用 SoapFormatter 時,還會啟用除多餘資料容錯之外的所有功能。如需使用這些類別進行序列化的詳細資訊,請參閱二進位序列化。
注意事項 |
|---|
| 將來,BinaryFormatter 的多餘資料容錯功能可能會做為 .NET Framework 1.1 的修補程式提供。 |
功能清單
這組功能包括下列項目:
-
多餘或非預期資料的容錯。這可讓較新版本的型別傳送資料至較舊版本。
-
遺失選擇性資料的容錯。這可讓較舊版本傳送資料至較新版本。
-
序列化回呼。遺失資料時,這可啟用智慧預設值設定。
此外,還有一個功能,可以在加入新的選擇性欄位時進行宣告。這個功能就是 OptionalFieldAttribute 屬性 (Attribute) 的 VersionAdded 屬性 (Property)。
下文中會詳細討論這些功能。
多餘或非預期資料的容錯
過去在還原序列化期間,任何多餘或非預期資料都會導致擲回例外狀況。而透過 VTS,在相同情況下,會忽略任何多餘或非預期資料,而不會導致擲回例外狀況。這可允許使用較新版本 (即包括更多欄位的版本) 型別的應用程式,將資訊傳送至需要同一個型別之較舊版本的應用程式。
在下列範例中,當較舊版本的應用程式還原序列化較新版本時,會忽略 2.0 版 Address 類別之 CountryField 中包含的多餘資料。
// Version 1 of the Address class. [Serializable] public class Address { public string Street; public string City; } // Version 2.0 of the Address class. [Serializable] public class Address { public string Street; public string City; // The older application ignores this data. public string CountryField; }
遺失資料的容錯
將 OptionalFieldAttribute 屬性套用至欄位,即可將其標記為選擇性。還原序列化期間,如果遺失選擇性資料,則序列化引擎會忽略此情況而不會擲回例外狀況。如此一來,需要較舊版本型別的應用程式,可以將資料傳送至需要同一型別之較新版本的應用程式。
下列範例顯示將 CountryField 欄位標記為選擇性的 2.0 版 Address 類別。如果較舊版本的應用程式傳送第 1 版至需要 2.0 版的較新版本應用程式,則會忽略遺失資料的情況。
序列化回呼
序列化回呼機制可提供在序列化/還原序列化處理序中的四個時機的攔截。
| 屬性 | 呼叫相關方法的時機 | 一般用法 |
|---|---|---|
| 還原序列化之前。* | 初始化選擇性欄位的預設值。 | |
| 還原序列化之後。 | 依據其他欄位的內容修正選擇性欄位值。 | |
| 序列化之前。 | 準備序列化。例如,建立選擇性資料結構。 | |
| 序列化之後。 | 記錄序列化事件。 |
* 此回呼會在還原序列化建構函式 (如果存在的話) 之前叫用。
使用回呼
若要使用回呼,請將適當的屬性套用至接受 StreamingContext 參數的方法。每個類別僅有一個方法可以使用每個屬性來標記。例如:
[OnDeserializing] private void SetCountryRegionDefault(StreamingContext sc) { CountryField = "Japan"; }
這些方法專門用於版本控制。還原序列化期間,如果遺失選擇性欄位的資料,則可能無法正確初始化選擇性欄位。這可以藉由建立指派正確值的方法,然後將 OnDeserializingAttribute 或 OnDeserializedAttribute 屬性套用至該方法來更正。
下列範例顯示型別內容中的方法。如果較舊版本的應用程式將 Address 類別的執行個體傳送至較新版本的應用程式,則會遺失 CountryField 欄位資料。但在還原序列化之後,該欄位將會設定為預設值 "Japan"。
VersionAdded 屬性
OptionalFieldAttribute 具有 VersionAdded 屬性。在 .NET Framework 2.0 版中,未使用此項。不過,正確設定這個屬性以確保型別與未來的序列化引擎相容很重要。
這個屬性表示指定欄位所加入至的型別版本。每次修改型別時,此值都應正好遞增 1 (從 2 開始),如下列範例所示:
// Version 1.0 [Serializable] public class Person { public string FullName; } // Version 2.0 [Serializable] public class Person { public string FullName; [OptionalField(VersionAdded = 2)] public string NickName; [OptionalField(VersionAdded = 2)] public DateTime BirthDate; } // Version 3.0 [Serializable] public class Person { public string FullName; [OptionalField(VersionAdded=2)] public string NickName; [OptionalField(VersionAdded=2)] public DateTime BirthDate; [OptionalField(VersionAdded=3)] public int Weight; }
最佳作法
若要確保正確的版本控制行為,請在修改以建立不同版本的型別時,遵循下列規則:
-
絕對不要移除序列化的欄位。
-
如果 NonSerializedAttribute 屬性在前一版中並未套用至某欄位,則絕對不要將它套用至該欄位。
-
絕對不要變更序列化欄位的名稱或型別。
-
加入新的序列化欄位時,請套用 OptionalFieldAttribute 屬性。
-
如果欄位在前一版中是不可序列化的欄位,則從該欄位移除 NonSerializedAttribute 屬性時,請套用 OptionalFieldAttribute 屬性。
-
針對所有選擇性欄位,除非 0 或 null 可以做為預設值,否則請使用序列化回呼來設定有意義的預設值。
為確保型別可與未來的序列化引擎相容,請遵循下列方針:
-
永遠正確地設定 OptionalFieldAttribute 屬性 (Attribute) 上的 VersionAdded 屬性 (Property)。
-
避免分支的版本控制。
