.NET简谈组件程序设计之(渗入序列化过程)

在本人的上一篇文章“.NET简谈组件程序设计之(初识序列化、持久化) ”中,我们基本上了解了什么叫序列化和持久化。通过系统为我们提供的服务,我们可以很方便的进行二进制序列化、SOAP协议序列化。

今天这篇文章是来讲解怎么运用一些高级的功能,在序列化、反序列化过程中进行一些控制。[王清培版权所有,转载请给出署名]

这里穿插一句题外话:其实在我们自己编写组件的时候真的有好多东西可以借鉴.NET平台的一些优点,它的功能都不是死的,可以订阅、可以切入,在我们编写组件的时候,我们其实也要好好考虑一些高级的特性。

上面这段话其实是为了铺垫用的,意思是说序列化组件在它工作的时候我们可以“参合”进去。

IFormatter格式器接口在工作的时候会去检查要序列化的对象是否用Serializable特性进行了标记,如果有,那么就进行深度递归遍历或者广度递归遍历所有成员,如果内部成员被NonSerialized禁止序列化特性标记,那么IFormatter将跳过该成员。在对象的内部所有的成员如果没有被禁止序列化,那么都会经过序列化工程,所以我们很难保证在特殊的对象上能否递归遍历序列化成功。

很典型的对象就是event事件对象,在订阅列表中我们不能保证所有的订阅者都能够被序列化,但是我们又想在反序列化的时候能初始化一些数据。

IDeserializationCallback接口


  1. using System;  
  2. using System.Runtime.InteropServices;  
  3.  
  4. namespace System.Runtime.Serialization  
  5. {  
  6.     // 摘要:  
  7.     //     指示在完成整个对象图形的反序列化时通知类。  
  8.     [ComVisible(true)]  
  9.     public interface IDeserializationCallback  
  10.     {  
  11.         // 摘要:  
  12.         //     在整个对象图形已经反序列化时运行。  
  13.         //  
  14.         // 参数:  
  15.         //   sender:  
  16.         //     开始回调的对象。当前未实现该参数的功能。  
  17.         void OnDeserialization(object sender);  
  18.     }  
  19. }  

IDeserializationCallback接口是反序列化时会执行的接口,接口里面只有一个OnDeserialization方法,系统在反序列化的时候会检查待序列化对象是否实现了IDeserializationCallback接口,如果实现了,那么系统就调用该接口中的OnDeserialization方法。[王清培版权所有,转载请给出署名]

那么这个方法我们有何用呢,我们来看代码;


  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Runtime.Serialization;  
  5.  
  6. namespace ConsoleApplication1.序列化和持久化  
  7. {  
  8.     [Serializable]  
  9.     public class MyClass : IDeserializationCallback  
  10.     {  
  11.         public MyClass() { }  
  12.         public string number = "MyClass状态";  
  13.  
  14.         [field: NonSerialized]//事件必须用field进行修饰  
  15.         public event EventHandler Event;  
  16.  
  17.  
  18.         #region IDeserializationCallback 成员  
  19.  
  20.         public void OnDeserialization(object sender)  
  21.         {  
  22.  
  23.         }  
  24.  
  25.         #endregion)  
  26.  
  27.          
  28.     }  
  29. }  

MyClass类中有一个Event事件对象,我们在它上面加了禁止序列化特性,前面的field是用来把event对象也当成字段来看待,因为NonSerialized特性只能用在field字段上。

我们实现IDeserializationCallback接口,这个接口的方法会再每次反序列化的时候执行。


  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Runtime.Serialization;  
  5. using System.Runtime.Serialization.Formatters.Binary;  
  6. using System.Runtime.Serialization.Formatters.Soap;  
  7. using System.Runtime.Serialization.Formatters;  
  8. using System.IO;  
  9.  
  10. namespace ConsoleApplication1.序列化和持久化  
  11. {  
  12.     public static class Program  
  13.     {  
  14.         public static void Main()  
  15.         {  
  16.             SoapFormatter formatter = new SoapFormatter();  
  17.             Stream stream = new FileStream("obj.xml", FileMode.Create, FileAccess.Write);  
  18.             using (stream)  
  19.             {  
  20.                 MyClass myclass = new MyClass();  
  21.                 myclass.Event += new EventHandler(myclass_Event);  
  22.                 formatter.Serialize(stream, myclass);  
  23.             }  
  24.  
  25.             Stream stream1 = new FileStream("obj.xml", FileMode.Open, FileAccess.Read);  
  26.             using (stream1)  
  27.             {  
  28.                 MyClass myclass = formatter.Deserialize(stream1) as MyClass;  
  29.             }  
  30.         }  
  31.  
  32.         static void myclass_Event(object sender, EventArgs e)  
  33.         {  
  34.  
  35.         }  
  36.     }  
  37. }  


 

我们在MyClass类中订阅了Event事件,如果我没有在MyClass类中的Event事件上加上禁止序列化特性,那么执行序列化的时候肯定是回报错的。

如果我们需要再对象MyClass存在的时候就需要有一个事件订阅者存在,比如对象内部的日志记录、消息发送等。我们就可以在OnDeserialization方法中进行处理。


  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Runtime.Serialization;  
  5.  
  6. namespace ConsoleApplication1.序列化和持久化  
  7. {  
  8.     [Serializable]  
  9.     public class MyClass : IDeserializationCallback  
  10.     {  
  11.         public MyClass() { }  
  12.         public string number = "MyClass状态";  
  13.  
  14.         [field: NonSerialized]//事件必须用field进行修饰  
  15.         public event EventHandler Event;  
  16.  
  17.  
  18.         #region IDeserializationCallback 成员  
  19.  
  20.         public void OnDeserialization(object sender)  
  21.         {  
  22.             Event += new EventHandler(MyClass_Event);  
  23.         }  
  24.  
  25.         void MyClass_Event(object sender, EventArgs e)  
  26.         {  
  27.             //对象内部处理  
  28.         }  
  29.  
  30.         #endregion)  
  31.  
  32.          
  33.     }  
  34. }  

 

序列化生命周期事件

在序列化和反序列化的过程中,系统会经历几个过程。大致分为下列四种,

序列化前(OnSerializing)、序列化后(OnSerialized)、反序列化前(OnDeserializing)、反序列化后(OnDeserialized),然后系统给我们留了入口,我们可以通过参与这几个方法来进行一些过程控制。请看代码;


  1. [OnSerializing]  
  2.        void OnSerializing(StreamingContext context)  
  3.        {  
  4.  
  5.        }  
  6.        [OnSerialized]  
  7.        void OnSerialized(StreamingContext context)  
  8.        {  
  9.  
  10.        }  
  11.        [OnDeserializing]  
  12.        void OnDeSerializing(StreamingContext context)  
  13.        {  
  14.  
  15.        }  
  16.        [OnDeserialized]  
  17.        void OnDeserizlized(StreamingContext context)  
  18.        {  
  19.  
  20.        }  

这几个特性就是用来标记序列化组件的过程的,系统会在处理的时候分别调用这几个方法,我们可以在几个方法中进行过程控制。StreamingContext是序列化流的对象,我们可以获取到序列化流的目标。

ISerializable接口


  1. using System;  
  2. using System.Runtime.InteropServices;  
  3.  
  4. namespace System.Runtime.Serialization  
  5. {  
  6.     // 摘要:  
  7.     //     允许对象控制其自己的序列化和反序列化过程。  
  8.     [ComVisible(true)]  
  9.     public interface ISerializable  
  10.     {  
  11.         // 摘要:  
  12.         //     使用将目标对象序列化所需的数据填充 System.Runtime.Serialization.SerializationInfo。  
  13.         //  
  14.         // 参数:  
  15.         //   info:  
  16.         //     要填充数据的 System.Runtime.Serialization.SerializationInfo。  
  17.         //  
  18.         //   context:  
  19.         //     此序列化的目标(请参见 System.Runtime.Serialization.StreamingContext)。  
  20.         //  
  21.         // 异常:  
  22.         //   System.Security.SecurityException:  
  23.         //     调用方没有所要求的权限。  
  24.         void GetObjectData(SerializationInfo info, StreamingContext context);  
  25.     }  
  26. }  

如果我们想更进一步的控制序列化和反序列化过程,那么我们就来实现ISerializable接口,通过这个接口我们基本上能控制序列化和反序列化的所有数据。
 

我们在MyClass类中加上这些代码。


  1. #region ISerializable 成员  
  2.  
  3.        public void GetObjectData(SerializationInfo info, StreamingContext context)  
  4.        {  
  5.            info.AddValue("number""手动添加的状态");  
  6.        }  
  7.        #endregion  
  8.  
  9.        //反序列化构造函数  
  10.        protected MyClass(SerializationInfo info, StreamingContext context)  
  11.        {  
  12.            this.number = (string)info.GetValue("number"typeof(string));  
  13.        } 

我们实现了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,如需转载请自行联系原作者



上一篇:phpMyAdmin 4.7.x CSRF 漏洞利用


下一篇:把 SAP UI5 应用部署到 SAP Kyma