概述:
序列化(serialization)是指將一個對象的目前狀態轉換成位元組流(a stream of bytes)的過程,而反序列化(deserialization)則指序列化過程的逆過程,將位元組流轉換成一個對象。初聽起來可能對此不太感興趣,但是使用序列化卻有許多重要的原因。一旦將某一對象序列化,得到的位元組可以儲存在檔案、資料庫,或記憶體中——只要是可以儲存的任何地方。需要恢複對象時,僅僅只需從它儲存的位置反序列化即可。對象固有的這種特性對於無狀態的Web應用程式是非常重要的,因為它允許重要的狀態資訊可以在使用者請求之間保留。
序列化也允許對象在應用程式邊界之間傳遞,編程人員可以將描述對象狀態的位元組流在網路上傳遞,並在另一端反序列化成一個匹配的對象。從本質上講,序列化允許對象以“數值”的方式傳遞給另一個應用程式。.NET遠程架構廣泛地在進程之間使用序列化來編組對象資料。
序列化不是.NET新引入的概念,Windows程式員同各種各樣的序列化打交道:MFC中的歸檔機制、Visuabl Basic的PropertyBag和COM的IPersistStream介面等,都具有類似的特性。那麼.NET序列化與這些方式又有什麼區別呢?一個字,簡單。在.NET以前的開發環境中,序列化是一個手工定製,對於類設計人員來說,實作類別的序列化並不困難,但是實現難度較大的設計,它就顯得能麻煩、耗時。在.NET中,該過程完全是自動進行的,使用者所做的就是使用Serializable特性來標記一個能序列化的類,運行庫做餘下的事。
.NET運行庫怎樣實現這一捷徑的呢?當然是利用鏡像,或更為準確地說是反射。前面提到,程式集含有名為中繼資料的自描述(self-describing)資料,程式集的中繼資料完整地表述了內部每一個類型,包括類型中每個私人欄位的資訊。通過反射過程,運行庫能識別中繼資料,並用它來決定類應怎樣被序列化。
使用Serializable特性實現序列化:
前面已經提到,如果允許運行庫序列化一個對象,必須將該類標記上Serializable特性。下面的代碼定義了兩個標記了Serialable特性的類。
[Serializable]
public class Car
{
private string mColor;
private int mTopSpeed;
//Reference to another object
private Radio mRadio;
[NonSerialized] //Runtime will not serialize this field
private string mNickName;
public Car(string nickName, int topSpeed, string color)
{
mNickName = nickName;
mTopSpeed = topSpeed;
mColor = color;
mRadio = new Radio();
}
}
[Serializable]
public class Radio
{
private int mVolume = 5;
}
如上所示,Car類的mNickName欄位用NonSerialized特性標註,指示運行庫進行序列化時跳過該項,而其他部分允許進行序列化。這裡注意,mRadio是對另一對象的引用,意味著運行庫必須對其序列化。事實上,當運行庫被告知要對某個對象序列化時,使用給定的對象作為根,建立一個對象圖表(類似於記憶體回收行程)。所有在圖表中的對象也必須可被序列化的,否則運行庫將引發異常。
下面的代碼舉例說明如何將Car對象序列化到一個檔案之中。
static void Main(string[] args)
{
Car myCar = new Car("Christine", 150, "Red");
FileStream mySoapFile = File.Create("Car.txt");
// Use a SOAP formatter object to serialize the object.
new SoapFormatter().Serialize(mySoapFile, myCar);
mySoapFile.Close();
}
運行庫不控制序列化資料的實際格式,但是有一個專門進行格式化的對象負責對序列化輸出進行格式化。.NET架構有兩種格式化程式:SOAP格式化程式和二進位格式化程式。在前面的例子中,SoapFormatter對象將對象序列化進一個SOAP報文,並將其發送到特定的流中。圖2-26顯示了在執行完這段代碼後Car.txt中的內容。
如果仔細查看圖2-26,很容易發現SOAP是如何描繪每個對象和它的資料成員。這種方式通過增加檔案大小來提高了可讀性。二進位格式化程式使用了更為緊湊的格式。如果需要在網路間發送對象,這種方式就更重要,尤其是在需要考慮網路頻寬而不強調可讀性的情況下。
通過調用SoapFormatter.Dewerialize方法,就可將該對象讀入記憶體中:
static void Rehydrate()
{
FileStream mySoapFile = File.Open("Car.txt", FileMode.Open);
Car myCar = (Car) new SoapFormatter().Deserialize(mySoapFile);
mySoapFile.Close();
}
圖2-26 使用SOAP格式化程式對Car對象進行序列化
註:摘自《NET分布式編程——C#篇 》