7

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

 2 years ago
source link: https://www.cnblogs.com/wangiqngpei557/archive/2011/09/05/2167751.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

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

这里穿插一句题外话:其实在我们自己编写组件的时候真的有好多东西可以借鉴.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;
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;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Runtime.Serialization.Formatters;
using System.IO;
namespace ConsoleApplication1.序列化和持久化
{
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方法中进行处理。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace ConsoleApplication1.序列化和持久化
{
[Serializable]
public class MyClass : IDeserializationCallback, ISerializable
{
public MyClass() { }
public string number = "MyClass状态";
[field: NonSerialized]//事件必须用field进行修饰
public event EventHandler Event;
#region IDeserializationCallback 成员
public void OnDeserialization(object sender)
{
}
#endregion)
}
}

序列化生命周期事件

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

序列化前(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接口

using System;
using System.Runtime.InteropServices;
namespace System.Runtime.Serialization
{
// 摘要:
//     允许对象控制其自己的序列化和反序列化过程。
[ComVisible(true)]
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类中加上这些代码。

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中的数据。[王清培版权所有,转载请给出署名]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK