关于对象的序列化
Serialization是.NET中一種實現對象持久性(Persistent)的機制。它是一個將對象中的數據轉換成一個單一元素(通常是Stream)的過程。它的逆過程是Deserialization。Serialization的核心概念是將一個對象的所有數據看作一個獨立的單元。
一般說來,在兩種情況下非常需要Serialization:1)當我們希望能夠將對象當前的狀態完整地保存到存儲介質中,以便我們以后能夠精確地還原對象時;2)當我們希望將對象從一個應用程序空間(Application domain)傳遞到另一個應用程序空間時。例如,Windows Form程序就是利用Serialization機制來實現剪貼板的copy & paste的。
.NET Framework支持兩種類型的Serialization:Shallow Serialization和Deep Serialization。
所謂Shallow Serialization是將對象的可讀寫(read-write)屬性的值轉換成字節流,而對象內部的數據(沒有通過read-write屬性暴露出來的數據)則不被轉換。XmlSerializer以及Web Services就使用這種技術。
Deep Serialization比Shallow Serialization更加徹底,因為它是將存儲在對象私有變量里的實際值拷貝到字節流里。而且Deep Serialization還將serialize整個object graph。也就是說,如果你的對象持有其他對象的引用,或者其他對象引用的集合,那么所有這些對象都將被Serialize。BinaryFormatter和SoapFormatter以及.NET Remoting都使用Deep Serialization技術,它甚至被有限地用于LosFormatter來產生存儲在Web Form頁中的狀態數據。
本文將著重于Deep Serialization。Serialization的過程
.NET Framework通過Reflection提供自動Serialization的機制。當一個對象被序列化(Serialized)的時候,它的類名,Assembly,以及類實例的所有數據成員都將被寫入存儲介質中。Serialization引擎保持對所有已經被序列化的對象引用的追蹤,以確保相同的對象引用最多只被序列化一次。
通常,一個Serialization過程會由formatter(例如BinaryFormatter)的Serialize方法引發。對象的Serialization過程按照以下規則進行:
1、?? 檢測以確保formatter是否擁有一個代理選擇器(surrogate selector)。如果有,檢查代理選擇器是否持有給定的對象類型。如果有,ISerializable.GetObjectData被調用。
2、?? 如果formatter沒有代理選擇器,或者代理選擇器沒有對象類型,檢查對象是否被用Serializable屬性標記。如果沒有,則拋出SerializationException異常。
3、?? 如果對象被標記為Serializable,檢查對象是否實現了ISerializable接口。如果實現了此接口,則GetObjectData被調用。
4、?? 如果對象沒有實現ISerializable接口,則使用默認的序列化策略,來序列化沒有用NonSerialized屬性標記的域。使你的class能夠被序列化
通過上面對Serialization過程的分析,我們可以看出,有兩種方式可以使一個class能夠被序列化:1)將此class簡單地標記為Serializable;2)為此class實現ISerializable接口,并將此class標記為Serializable。1、?? 標記Serializable屬性
標記Serializable屬性的方式是實現Serialization的基本方法。舉個簡單的例子:
????[Serializable]
????public class Person
????{
????????public string name = null;
????????public int age = 0;
????}
你可以使用BinaryFormatter來將上面的class序列化:
????????Person sam = new Person();
????????sam.name = "sam";
????????sam.age = 24;
????????IFormatter formatter = new BinaryFormatter();
????????Stream stream = new FileStream("sam.dat",
????????????????????FileMode.Create, FileAccess.Write, FileShare.None);
????????formatter.Serialize(stream, sam);
????????stream.Close();
就是這么簡單,你所要做的就是創建一個Stream和一個formatter的實例,然后調用formatter的Serialize方法。經過BinaryFormatter serialize的數據仍然能夠通過BinaryFormatter deserialize回來,方法與serialize同樣簡單,這里就不贅述了。
如果你不想將類里的所有域都序列化,可以使用NonSerialized屬性進行選擇。如:
????[Serializable]
????public class Person
????{
????????public string name = null;
????????[NonSerialized]
????????public int age = 0;
????}
這樣,age域就不會被序列化了。
需要注意的是,Serializable屬性并不能被繼承。也就是說如果你希望Person的派生類也能夠被Serialize的話,那么這個派生類也必須被Serializable標記。否則將得到SerializationException異常。
同樣的,Person類中的所有對其他類的引用,其所引用的類都應該是能夠被Serialize的。.NET Framework中的大部分class都實現了ISerializable接口,但有些class沒有實現,例如ImageList。可以通過MSDN Library的到一個實現了ISerializable接口的class列表。對那些沒有實現此接口的class,使用的時候要當心。2、?? 實現ISerializable接口
Serializable屬性的功能非常強大,它使得Serialize和Deserialize變得十分簡單。但凡事有利必有弊,由Serializable實現的自動序列化方法有時不夠靈活。我們并不能完全控制Serialize和Deserialize的行為,而有些時候它們的行為對我們來說很重要。那么我們通過何種方法能夠控制Serialize和Deserialize的行為呢?答案就是,自己來實現ISerializable接口。ISerializable接口給予我們更大的自由來控制Serialize和Deserialize,但是無疑我們將不得不寫更多的代碼L。
下面我們來看看如何實現ISerializabe接口。ISerializable接口位于System.Runtime.Serialization名字空間中,聲明如下:
public inferface ISerializable?????? {
void GetObjectData(SerializationInfo info,
????????????????????????????StreamingContext context);?????? }
它只有一個方法GetObjectData。因此,像實現其他接口一樣,我們必須實現此方法。但與其他接口不同的是,為了Deserialization,我們還必須實現一個特殊的構造函數(我稱此構造函數為“序列化構造函數”),此構造函數具有與GetObjectData相同的參數列表。由于此構造函數專門用于.NET Framework在Deserialize時的Reflection機制,因此我們通常將它聲明為保護或私有模式。如下:(當然,如果你的class只需要Serialize而不需要Deserialize的話,也可以不實現這個特殊的構造函數)
????[Serializable]
????public class Person : ISerializable
????{
????????public string name = null;
????????public int age = 0;
????????public Person()
????????{
????????}
????????protected Person(SerializationInfo info, StreamingContext context)
????????{
????????????name = info.GetString("name");
????????????age = info.GetInt32("age");
????????}
????????void ISerializable.GetObjectData(SerializationInfo info,
????????????????????????????????????????StreamingContext context)
????????{
????????????info.AddValue("name", name);
????????????info.AddValue("age", age);
????????}
????}
通過實現ISerializable接口,使得我們有機會在ISerializable.GetObjectData中控制Serialize的行為,在“序列化構造函數”中控制Deserialize的行為。這個接口提供給我們的信息非常全面而靈活,以致于我們甚至可以在這兩個方法中耍些花招。比如,我們可以在Deserialize的時候,籍由改變info.FullTypeName來得到一種與被Serialize的對象不同類型的另一個對象等。獨辟蹊徑
前面談到過Serialization被運用的典型環境,是對象存儲、進程間數據傳遞等涉及到對象持久性的領域。但實際上,它也能夠被運用到其他的許多地方,關鍵在于我們是否能想到去用運Serialization,有時候思維定式也是很可怕的J。舉個例子,我們來看看在Clone方法中如何使用Serialization[1]。
如果我們要為Person類實現Clone方法,我們通常會這樣寫:
????[Serializable]
????public class Person : ICloneable
????{
????????public string name = null;
????????public int age = 0;
????????public object Clone()
????????{
????????????Person person = new Person();
????????????person.name = name;
????????????person.age = age;
????????????return person;
????????}
????}
如果我們利用Serialization的方法,Clone函數就能寫成下面的樣子:
????????public object Clone()
????????{
????????????MemoryStream stream = new MemoryStream();
????????????BinaryFormatter formatter = new BinaryFormatter();
????????????formatter.Serialize(stream, this);
????????????stream.Position = 0;
????????????return formatter.Deserialize(stream);
????????}
從這兩個實現上看,使用Serialization實現Clone方法似乎并沒有什么好處。可是設想如果你面對的是一個復雜的類繼承體系,從基類到派生類都需要實現Clone方法。利用第一種實作手法,你將不得不為每一個class寫一個Clone方法,而且隨著數據成員的增多,這個方法將越來越冗長,并且會由于數據成員的改變而引發錯誤(我曾經遇到過好幾次,由于class中增加了成員變量,而Clone方法沒有及時更新,導致運行時錯誤。呵呵,這種錯誤還很難調試)。現在你看到用Serialization實現的好處了吧?是的,我們只要在基類中將Clone方法聲明為virtual,并用Serialization的方法實現之,然后保證基類和派生類都可以被Serialize,上面所有的麻煩不都迎刃而解了嗎?總結
現代軟件項目中,無論何種項目都會或多或少地涉及到對象持久性的問題,.NET也不例外,無論是Windows Form、ASP.NET,還是Web Services,都需要處理對象持久性。而Serialization正是.NET為應對這個問題而給出的解法。
總結
- 上一篇: java对象序列化java.io.Ser
- 下一篇: 对象的序列化