今天這篇文章是來講解怎麼運用一些進階的功能,在序列化、反序列化過程中進行一些控制。[王清培版權所有,轉載請給出署名]
這裡穿插一句題外話:其實在我們自己編寫元件的時候真的有好多東西可以借鑒.NET平台的一些優點,它的功能都不是死的,可以訂閱、可以切入,在我們編寫元件的時候,我們其實也要好好考慮一些進階的特性。
上面這段話其實是為了鋪墊用的,意思是說序列化元件在它工作的時候我們可以“參合”進去。
IFormatter格式器接口在工作的時候會去檢查要序列化的對象是否用Serializable特性進行了标記,如果有,那麼就進行深度遞歸周遊或者廣度遞歸周遊所有成員,如果内部成員被NonSerialized禁止序列化特性标記,那麼IFormatter将跳過該成員。在對象的内部所有的成員如果沒有被禁止序列化,那麼都會經過序列化工程,是以我們很難保證在特殊的對象上能否遞歸周遊序列化成功。
很典型的對象就是event事件對象,在訂閱清單中我們不能保證所有的訂閱者都能夠被序列化,但是我們又想在反序列化的時候能初始化一些資料。
IDeserializationCallback接口
using System;
using System.Runtime.InteropServices;
namespace System.Runtime.Serialization
{
// 摘要:
// 訓示在完成整個對象圖形的反序列化時通知類。
[ComVisible(true)]
public interface IDeserializationCallback
{
// 摘要:
// 在整個對象圖形已經反序列化時運作。
//
// 參數:
// sender:
// 開始回調的對象。目前未實作該參數的功能。
void OnDeserialization(object sender);
}
}
IDeserializationCallback接口是反序列化時會執行的接口,接口裡面隻有一個OnDeserialization方法,系統在反序列化的時候會檢查待序列化對象是否實作了IDeserializationCallback接口,如果實作了,那麼系統就調用該接口中的OnDeserialization方法。[王清培版權所有,轉載請給出署名]
那麼這個方法我們有何用呢,我們來看代碼;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace ConsoleApplication1.序列化和持久化
[Serializable]
public class MyClass : IDeserializationCallback
public MyClass() { }
public string number = "MyClass狀态";
[field: NonSerialized]//事件必須用field進行修飾
public event EventHandler Event;
#region IDeserializationCallback 成員
public void OnDeserialization(object sender)
{
}
#endregion)
MyClass類中有一個Event事件對象,我們在它上面加了禁止序列化特性,前面的field是用來把event對象也當成字段來看待,因為NonSerialized特性隻能用在field字段上。
我們實作IDeserializationCallback接口,這個接口的方法會再每次反序列化的時候執行。
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Runtime.Serialization.Formatters;
using System.IO;
public static class Program
public static void Main()
SoapFormatter formatter = new SoapFormatter();
Stream stream = new FileStream("obj.xml", FileMode.Create, FileAccess.Write);
using (stream)
{
MyClass myclass = new MyClass();
myclass.Event += new EventHandler(myclass_Event);
formatter.Serialize(stream, myclass);
}
Stream stream1 = new FileStream("obj.xml", FileMode.Open, FileAccess.Read);
using (stream1)
MyClass myclass = formatter.Deserialize(stream1) as MyClass;
static void myclass_Event(object sender, EventArgs e)
我們在MyClass類中訂閱了Event事件,如果我沒有在MyClass類中的Event事件上加上禁止序列化特性,那麼執行序列化的時候肯定是回報錯的。
如果我們需要再對象MyClass存在的時候就需要有一個事件訂閱者存在,比如對象内部的日志記錄、消息發送等。我們就可以在OnDeserialization方法中進行處理。
Event += new EventHandler(MyClass_Event);
void MyClass_Event(object sender, EventArgs e)
//對象内部處理
序列化生命周期事件
在序列化和反序列化的過程中,系統會經曆幾個過程。大緻分為下列四種,
序列化前(OnSerializing)、序列化後(OnSerialized)、反序列化前(OnDeserializing)、反序列化後(OnDeserialized),然後系統給我們留了入口,我們可以通過參與這幾個方法來進行一些過程控制。請看代碼;
[OnSerializing]
void OnSerializing(StreamingContext context)
{
}
[OnSerialized]
void OnSerialized(StreamingContext context)
[OnDeserializing]
void OnDeSerializing(StreamingContext context)
[OnDeserialized]
void OnDeserizlized(StreamingContext context)
這幾個特性就是用來标記序列化元件的過程的,系統會在處理的時候分别調用這幾個方法,我們可以在幾個方法中進行過程控制。StreamingContext是序列化流的對象,我們可以擷取到序列化流的目标。
ISerializable接口
// 允許對象控制其自己的序列化和反序列化過程。
public interface ISerializable
// 使用将目标對象序列化所需的資料填充 System.Runtime.Serialization.SerializationInfo。
// info:
// 要填充資料的 System.Runtime.Serialization.SerializationInfo。
// context:
// 此序列化的目标(請參見 System.Runtime.Serialization.StreamingContext)。
// 異常:
// System.Security.SecurityException:
// 調用方沒有所要求的權限。
void GetObjectData(SerializationInfo info, StreamingContext context);
如果我們想更進一步的控制序列化和反序列化過程,那麼我們就來實作ISerializable接口,通過這個接口我們基本上能控制序列化和反序列化的所有資料。
我們在MyClass類中加上這些代碼。
#region ISerializable 成員
public void GetObjectData(SerializationInfo info, StreamingContext context)
info.AddValue("number", "手動添加的狀态");
#endregion
//反序列化構造函數
protected MyClass(SerializationInfo info, StreamingContext context)
this.number = (string)info.GetValue("number", typeof(string));
}
我們實作了ISerializable接口,裡面隻有一個方法GetObjectData,這個方法我想是系統要調用的,是用來擷取序列化資料對的,我們通過Serializationinfo對象來進行設定。其實SerializationInfo是對StreamingContext對象的包裝,主要的目的就是用來進行資料的設定的。StreamingContext是序列化流的引用,最後是要将這些資料寫入Stream中的。
有一個至關重要的地方就是,在系統進行反序列化的時候不會調用Serializable特性标記的對象的預設構造函數,因為系統也不确定在構造函數是否能恢複對象的所有的資料,因為在序列化的時候可能過濾了部分NonSerializable标記對象。是以系統會調用自己規定的以個重載構造函數,就是我上面所寫的:
protected MyClass(SerializationInfo info, StreamingContext context)
系統通過Serializationinfo和StreamingContext兩個對象來恢複當初序列化到StreamContext中的資料。[王清培版權所有,轉載請給出署名]
本文轉自 王清培 51CTO部落格,原文連結:http://blog.51cto.com/wangqingpei557/658257,如需轉載請自行聯系原作者