標籤:
.NET Framework的序列化架構相當全面,本節要討論如何設計一個類型,本節要討論如何設計一個類型,它能將自己的序列化或還原序列化成一個不同的類型或對象。下面列舉一些有趣的例子。
1、 有的類型(比如System.DBNull和System.Reflection.Missing)設計成每個AppDomain一個執行個體。我們經常把這些類型稱為單一實例類型。給定一個DBNull對象引用,序列化和還原序列化它不應造成在AppDomain中建立一個DBNull對象。序列化後,返回的引用應指向AppDomain中現有的DBNull對象。
2、 對於某些類型來說(比如System.Type,System.Reflection.Assembly和其他反射類型,比如MemberInfo),每個類型、每個程式集或每個成員等只有一個執行個體。例如,假定一個數組中的每個元素都引用一個MemberInfo對象,其中有5個元素都引用一個MemberInfo對象。在序列化和還原序列化這個數組之後,當初引用一個MemberInfo對象的5個元素現在還是應該引用一個MemberInfo對象。除此之外,這些元素引用的那個MemberInfo對象還必須實際對應於AppDomain中的一個特定的成員。在輪詢資料庫連接對象或者其他任何類型的對象時,也可以利用這個單一實例功能。
3、 對於遠端控制的對象,CLR序列化與伺服器對象有關的資訊。在用戶端上還原序列化時,會造成CLR建立一個代理對象。這個代理對象的類型有別於伺服器對象的類型,但這對於用戶端代碼來說是透明的。用戶端直接在代理對象上調用執行個體方法。然後,代理代碼內部會將調用遠程發送給伺服器,有後者實際執行請求的操作。
下面來看看一些範例程式碼,他們展示了如何正確序列化和還原序列化一個單一實例類型:
[Serializable] public sealed class Singleton : ISerializable { private static readonly Singleton s_theOneObject = new Singleton(); public string Name = "Jeff"; public DateTime date = DateTime.Now; // private Singleton() { } public static Singleton GetSingleton() { return s_theOneObject; } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(SingleSerializationHelper)); } [Serializable] private sealed class SingleSerializationHelper : IObjectReference { //這個方法在對象還原序列化之後調用 public Object GetRealObject(StreamingContext context) { return Singleton.GetSingleton(); } } }
Singleton類所代表的類型規定每個AppDomain只能存在它自己的一個執行個體。以下代碼測試Singleton的序列化和還原序列化代碼,保證AppDomain中存在Singleton類型的一個執行個體:
private static void SingletonSerializationTest() { Singleton[] a1 = { Singleton.GetSingleton(), Singleton.GetSingleton() }; Console.WriteLine("do both elements refer to the same object ?" + (a1[0] == a1[1]));//true using (var stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, a1); stream.Position = 0; Singleton[] a2 = (Singleton[])formatter.Deserialize(stream); Console.WriteLine("do both elements refer to the same object ?" + (a2[0] == a2[1]));//true Console.WriteLine("do both elements refer to the same object ?" + (a2[0] == a2[0]));//true } }
現在,我們通過分析代碼來理解所發生的事情。Singleton類型載入到AppDomain中時,CLR調用它的靜態構造器,它構造一個對象,並將對象的引用儲存到靜態欄位s_theOneObject中。Singleton類沒有提供任何公用構造器,這防止了其他任何代碼構造類的其他執行個體。
在SingletonSerializationTest中,我們建立了包含兩個元素的一個數組;每個元素都引用Singleton對象。為了初始化兩個元素,我們調用Singleton的靜態GetSingleton方法。這個方法返回對一個Singleton對象的引用。對Console的WriteLine方法的第一個調用顯示true,證明兩個元素引用的是同一個對象。
現在SingletonSerializationTest調用格式化器的Serialize方法序列化數組及其元素。序列化第一個Singleton時,格式化器檢測Singleton類型實現了ISerializable介面,並調用GetObjectData方法。這個方法調用SetType,向它傳遞SingleSerializationHelper類型,告訴格式化器將Singleton對象序列化成一個SingleSerializationHelper對象。由於AddValue沒有調用,所以沒有額外的欄位資訊寫入流。由于格式化器自動檢測出兩個數組元素都引用一個對象,所以格式化器只能序列化一個對象。
序列化數組之後,SingletonSerializationTest調用格式化器的Deserialize方法。對流進行還原序列化時,格式化器嘗試還原序列化一個SingleSerializationHelper對象,這是格式化器之前被“欺騙”所序列化的東西。構造好SingleSerializationHelper對象夠,格式化器發現這個類型實現了IObjectReference介面,格式化器會調用GetRealObject方法。這個方法返回在對象還原序列化好之後你整整想要引用的對象。在我的例子中,SingleSerializationHelper類型讓GetRealObject返回對AppDomain中已存在的Singleton中已存在的Singleton對象的一個引用。所以,當格式化器的Deserialize方法返回時,a2數組包含兩個元素,兩者都引用Appdomin的Singleton對象。
第七節:將類型序列化為不同的類型以及將類型還原序列化為不同的對象