.NET Framework提供了將對象序列化和還原序列化的能力。利用這種機制,我們可以將對象執行個體的狀態儲存到儲存媒體上,也可以將對象從一個地方傳遞到另一個地方。
.NET Framework提供了一些用於序列化的類。一個是BinnaryFormatter,它使用二進位格式序列化對象。另一個是SoapFormatter,它使用soap格式(基於XML格式)序列化對象。還可以使用XMLSerializer將對象序列化成XML格式。然而這種序列化機制是有一定限制的,它只能序列化特定的對象。這些對象要麼是從MarshalByRefObject派生的對象,要麼是標記為Serializable的簡單類型對象。像System.Windows.Forms.Control這類複雜的對象就不能被支援。
今天我要介紹的是如何序列化System.Windows.Forms.Control這類複雜的對象。在介紹之前,我們先看看序列化和還原序列化的流程。
分離出複雜的邏輯,最簡單的邏輯只有兩步,第一步-序列化過程:將記憶體中的對象資料用XML格式的資料表示。第二步-還原序列化過程:構造一個新對象,並將XML格式表示的資料賦值給這個新的對象。這樣,原始對象和新構造的對象會有同樣的資料,即表示同一種狀態。
經常使用VS2005,VS2008等IDE的開發人員,特別是做過Design Time開發的開發人員可能會注意到,這個過程其實和IDE自動產生控制項代碼的過程相似。
在使用IDE的過程中,我們在Design環境中給Form拖一個TextBox控制項,IDE會在Design環境中建立一個TextBox控制項,並產生相關C#或者VB等格式的代碼。這個階段叫設計時(DesignTime)。這個過程類似我們剛才講到的第一步-序列化過程。而我們點擊啟動並執行時候,這些文字格式設定的代碼又會被編譯運行,根據編譯的代碼產生一個新TextBox控制項在Form上顯示。這個階段叫運行時(RunTime)。這個過程就類似我們剛才講到的第二步-還原序列化過程。可以看出來,IDE在DesignTime下是將控制項執行個體序化成C#或VB等代碼。而在RunTime下是將代碼編譯運行,產生新的控制項執行個體來保持DesignTime下設計的狀態與RunTime下顯示的狀態一致。事實上,這也是序列化和還原序列化的一種方式。
既然IDE的這個過程也是序列化和還原序列化的方式,那麼IDE是怎麼實現複雜控制項序列化和還原序列化的呢?這可能需要另外一篇文章才能講清楚,這裡暫不研究。不過我們可以知道,IDE是有能力做這件事情的。我們也可以利用IDE的這種能力,來實現複雜控制項的序列化和還原序列化。
下面這段代碼示範了如何利用IDE的這種能力序列化一個Object。
SaveObject
1 /**//// <summary>
2 /// Saves the <see cref="T:Object"/> to the given stream.
3 /// </summary>
4 /// <param name="value">
5 /// The <see cref="T:Object"/> will be serialized to save.
6 /// </param>
7 /// <param name="stream">
8 /// The stream to which the <see cref="T:Object"/> will be serialized.
9 /// </param>
10 public static void SaveObject(object value, Stream stream)
11 {
12 if (stream == null)
13 {
14 throw new ArgumentNullException();
15 }
16
17 using (DesignSurface designSurface = new DesignSurface())
18 {
19 designSurface.BeginLoad(new DefaultCodeDomDesignerLoader());
20 ComponentSerializationService serializationService = designSurface.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;
21
22 SerializationStore store = serializationService.CreateStore();
23 serializationService.Serialize(store, value);
24 store.Save(stream);
25 }
26 }
27
下面這段代碼示範了如何利用IDE的這種能力還原序列化一個Object。
LoadObject
1 /**//// <summary>
2 /// Loads a <see cref="T:Object"/> from the given stream.
3 /// </summary>
4 /// <param name="stream">
5 /// The stream from which to load the <see cref="T:Object"/>.
6 /// </param>
7 /// <returns>
8 /// The loaded <see cref="T:Object"/>.
9 /// </returns>
10 public static object LoadObject(Stream stream)
11 {
12 if (stream == null)
13 {
14 throw new ArgumentNullException();
15 }
16
17 using (DesignSurface designSurface = new DesignSurface())
18 {
19 designSurface.BeginLoad(new DefaultCodeDomDesignerLoader());
20 ComponentSerializationService serializationService = designSurface.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;
21
22 SerializationStore store = serializationService.LoadStore(stream);
23 ICollection collection = serializationService.Deserialize(store);
24
25 foreach (IComponent associatedComponent in designSurface.ComponentContainer.Components)
26 {
27 designSurface.ComponentContainer.Remove(associatedComponent);
28 }
29
30 IEnumerator enumerator = collection.GetEnumerator();
31 if (enumerator.MoveNext())
32 {
33 return enumerator.Current;
34 }
35
36 return null;
37 }
38 }
利用IDE的這種能力,我們還可以做很多事情,比如深度複製控制項。有時候,我們需要複製一個複雜的控制項,然而辦法有限,要麼使用反射枚舉控制項所有欄位賦值,要麼給所有屬性一個一個賦值。而屬性之間的依賴關係,參考型別的屬性如何處理等都是難題。利用控制項序列化和還原序列化,可以輕鬆實現控制項的複製。再比如在RunTime下得到控制項的C#,VB等代碼,因為序列化的中間產物就是C#,VB等代碼,所以利用這個機制很容易實現。這些代碼我寫了一個類庫,需要的可以和我聯絡。代碼太多,在這裡就不貼了。
補充:現在我已經把原始碼上傳了。可以在這裡下載。
原創文章,轉載請註明出處。KevinShan Email:txhak@163.com。