天天看點

.NET簡談元件程式設計之(滲入序列化過程)

今天這篇文章是來講解怎麼運用一些進階的功能,在序列化、反序列化過程中進行一些控制。[王清培版權所有,轉載請給出署名]

這裡穿插一句題外話:其實在我們自己編寫元件的時候真的有好多東西可以借鑒.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,如需轉載請自行聯系原作者

繼續閱讀