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