前一段時間因為頻繁使用類的序列化,所以一直在思考如何能更方便的使用這個功能,現在把一段時間的學習過程和大家分享,同時還有個疑問向大家請教,如果您已經非常熟悉類的序列化,那麼請您直接看第三部分。
什麼是類的序列化?說白了,就是把一個類的執行個體轉化成一段XML格式或二進位格式的資料,以便於網路傳輸、儲存等操作。
同理,還原序列化就是把XML或者二進位描述的對象還原成一個類的執行個體。
零、開始序列化
在C#中,要實作類別的序列化並不難,以XML序列化為例,首先我們聲明一個類:
[Serializable]
public class myClass
{
......
}
其中類聲明上面的一句[Serializable]用來指示此類是可以序列化的,然後
引用兩個namespace:
using System.IO;
using System.Xml.Serialization;
於是就可以執行下面代碼:
1Code
2myClass cls = new myClass();
3XmlSerializer xmlSerializer = new XmlSerializer(cls.GetType());
4MemoryStream stream = new MemoryStream();
5xmlSerializer.Serialize(stream, cls);
6byte[] buf = stream.ToArray();
7string xml = Encoding.ASCII.GetString(buf);
8stream.Close();
自此,序列化就完成了,XML序列儲存在string xml;變數中。
上述代碼不難理解,XmlSerializer類用來提供XML序列化的功能,xmlSerializer.Serialize(stream, cls)方法,可以把類cls序列化,並將XML序列儲存在流stream中。
以上便是序列化的基本方法,可以滿足我們的需求。但問題是如果我們經常需要對數個類進行序列化和還原序列化,就要頻繁的重複上述代碼,能不能讓類具備自己進行序列化的方法呢?
一、第一次嘗試
我首先想到,可以寫一個基類,提供進行序列化的方法,任何想實現序列化的類,只需要繼承此類就可以具備此方法。於是構造抽象類別:
1 public abstract class SerializableBaseClass
2 {
3 public virtual string XMLSerialize()
4 {
5 XmlSerializer xmlSerializer = new XmlSerializer(GetType()); //差異1
6 MemoryStream stream = new MemoryStream();
7 xmlSerializer.Serialize(stream, this); //差異2
8 byte[] buf = stream.ToArray();
9 string xml = Encoding.ASCII.GetString(buf);
10 stream.Close();
11 return xml;
12 }
13 }
上面一段代碼和之前的代碼只有兩行不一樣:
差異1:
cls.GetType()變成GetType():其中GetType()是擷取當前執行個體的類型,在基類中調用GetType()得到的是當前執行個體的類型,而不是積累的類型。也就是說上述基類中調用GetType()不會得到” SerializableBaseClass”,更不會是”System.Object”,而是當前對象執行個體的類型。
差異2:
cls變成this:同理,this引用是指向當前執行個體的,只不過在基類中,不能使用this直接存取子類成員。當然通過類型轉換可以達到此目的,但不在本文討論範圍內。所以Serialize(stream,this)會將整個對象序列化,而不會造成對象分割,只把基類給序列化了。
二、第二次嘗試
到此為止,任何類只要整合了SerializableBaseClass就擁有了自我序列化的方法,但如何還原序列化呢?我們在SerializableBaseClass類中再添加一個方法。
1 public object DeSerialize(string xmlString)
2 {
3 XmlSerializer xmlSerializer = new XmlSerializer(GetType());
4 byte[] buf = Encoding.ASCII.GetBytes(xmlString);
5 MemoryStream stream = new MemoryStream(buf);
6 object o = xmlSerializer.Deserialize(stream);
7 return o;
8 }
此方法就實現了類的還原序列化,我們可以這樣使用:
聲明:
[Serializable]
public class myClass : SerializableBaseClass
{
……
}
使用:
1 myClass cls = new myClass();
2 string xml = cls. Serialize();
3 myClass cls1 = (myClass)cls. DeSerialize(xml);
這個使用乍一看沒什麼問題,但實際使用起來就很蹩腳
1、要想還原序列化一個類,先要建立這個類的執行個體,只為了調用DeSerialize()
2、調用DeSerialize()返回的是object類型,需要進行類型轉換
對於這兩個問題,我也考慮過很久,如果把DeSerialize()定義為static,那麼就不能使用GetType()方法,而其,也無法擷取子類的類型。
三、最後的嘗試
對於上述兩個問題,想了很久,始終沒想到解決辦法,直到一天和朋友討論c++STL的某個問題的時候,終於茅塞頓開,c#也是支援模板的呀,於是激動不已,改寫SerializableBaseClass類:
1 [Serializable()]
2 public abstract class SerializableBaseClass <T>
3 {
4 public virtual string XMLSerialize()
5 {
6 XmlSerializer xmlSerializer = new XmlSerializer(GetType());
7 MemoryStream stream = new MemoryStream();
8 xmlSerializer.Serialize(stream, this);
9 byte[] buf = stream.ToArray();
10 string xml = Encoding.ASCII.GetString(buf);
11 stream.Close();
12 return xml;
13 }
14
15 public static T DeSerialize(string xmlString)
16 {
17 XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
18 byte[] buf = Encoding.ASCII.GetBytes(xmlString);
19 MemoryStream stream = new MemoryStream(buf);
20 T o = (T)xmlSerializer.Deserialize(stream);
21 return o;
22 }
23 }
24
這樣,問題就不完美的解決了(確實不完美)。為什麼不完美呢?那就看下面的代碼:
[Serializable]
public class myClass : SerializableBaseClass<myClass>
{
……
}
使用:
myClass cls = myClass.DeSerialize(xmlData);
看似上述問題都解決了,但我還是心裡不爽,因為每次定義一個繼承SerializableBaseClass的類,還必須把自己的類名再寫一遍,放在模板類型的參數裡。有沒有一種方案,可以讓上述myClass類的聲明預設範本參數就是自身myClass,而無須再寫一遍<myClass>呢?或者還有更好的其他方法?歡迎討論。