NET Framework提供了将对象序列化和反序列化的能力。利用这种机制,我们可以将对象实例的状态存储到存储媒体上,也可以将对象从一个地方传递到另一个地方。.NET Framework提供了一些用于序列化的类。一个是BinnaryFormatter,它使用二进制格式序列化对象。另一个是SoapFormatter,它使用soap格式(基于XML格式)序列化对象。还可以使用XMLSerializer将对象序列化成XML格式。然而这种序列化机制是有一定限制的,它只能序列化特定的对象。这些对象要么是从MarshalByRefObject派生的对象,要么是标记为Serializable的简单类型对象。像System.Windows.Forms.Control这类复杂的对象就不能被支持。
今天我要介绍的是如何序列化System.Windows.Forms.Control这类复杂的对象。在介绍之前,我们先看看序列化和反序列化的流程。
分离出复杂的逻辑,最简单的逻辑只有两步,第一步-序列化过程:将内存中的对象数据用XML格式的数据表示。第二步-反序列化过程:构造一个新对象,并将XML格式表示的数据赋值给这个新的对象。这样,原始对象和新构造的对象会有同样的数据,即表示同一种状态。
经常使用VS2005,VS2008等IDE的开发人员,特别是做过Design Time开发的开发人员可能会注意到,这个过程其实和IDE自动生成控件代码的过程相似。
在使用IDE的过程中,我们在Design环境中给Form拖一个TextBox控件,IDE会在Design环境中创建一个TextBox控件,并生成相关C#或者VB等格式的代码。这个阶段叫设计时(DesignTime)。这个过程类似我们刚才讲到的第一步-序列化过程。而我们点击运行的时候,这些文本格式的代码又会被编译运行,根据编译的代码生成一个新TextBox控件在Form上显示。这个阶段叫运行时(RunTime)。这个过程就类似我们刚才讲到的第二步-反序列化过程。可以看出来,IDE在DesignTime下是将控件实例序化成C#或VB等代码。而在RunTime下是将代码编译运行,生成新的控件实例来保持DesignTime下设计的状态与RunTime下显示的状态一致。事实上,这也是序列化和反序列化的一种方式。
既然IDE的这个过程也是序列化和反序列化的方式,那么IDE是怎么实现复杂控件序列化和反序列化的呢?这可能需要另外一篇文章才能讲清楚,这里暂不研究。不过我们可以知道,IDE是有能力做这件事情的。我们也可以利用IDE的这种能力,来实现复杂控件的序列化和反序列化。
下面这段代码演示了如何利用IDE的这种能力序列化一个Object。
SaveObject
1
/// <summary>
2
/// Saves the <see cref="T
bject"/> to the given stream.
3
/// </summary>
4
/// <param name="value">
5
/// The <see cref="T
bject"/> will be serialized to save.
6
/// </param>
7
/// <param name="stream">
8
/// The stream to which the <see cref="T
bject"/> will be serialized.
9
/// </param>
10
public static void SaveObject(object value, Stream stream)
11
{
12
if (stream == null)
13
{
14
throw new ArgumentNullException();
15
}
16
17
using (DesignSurface designSurface = new DesignSurface())
18
{
19
designSurface.BeginLoad(new DefaultCodeDomDesignerLoader());
20
ComponentSerializationService serializationService = designSurface.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;
21
22
SerializationStore store = serializationService.CreateStore();
23
serializationService.Serialize(store, value);
24
store.Save(stream);
25
}
26
}
27
下面这段代码演示了如何利用IDE的这种能力反序列化一个Object。
LoadObject
1
/// <summary>
2
/// Loads a <see cref="T
bject"/> from the given stream.
3
/// </summary>
4
/// <param name="stream">
5
/// The stream from which to load the <see cref="T
bject"/>.
6
/// </param>
7
/// <returns>
8
/// The loaded <see cref="T:Object"/>.
9
/// </returns>
10
public static object LoadObject(Stream stream)
11
{
12
if (stream == null)
13
{
14
throw new ArgumentNullException();
15
}
16
17
using (DesignSurface designSurface = new DesignSurface())
18
{
19
designSurface.BeginLoad(new DefaultCodeDomDesignerLoader());
20
ComponentSerializationService serializationService = designSurface.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;
21
22
SerializationStore store = serializationService.LoadStore(stream);
23
ICollection collection = serializationService.Deserialize(store);
24
25
foreach (IComponent associatedComponent in designSurface.ComponentContainer.Components)
26
{
27
designSurface.ComponentContainer.Remove(associatedComponent);
28
}
29
30
IEnumerator enumerator = collection.GetEnumerator();
31
if (enumerator.MoveNext())
32
{
33
return enumerator.Current;
34
}
35
36
return null;
37
}
38
}
利用IDE的这种能力,我们还可以做很多事情,比如深度克隆控件。有时候,我们需要克隆一个复杂的控件,然而办法有限,要么使用反射枚举控件所有字段赋值,要么给所有属性一个一个赋值。而属性之间的依赖关系,引用类型的属性如何处理等都是难题。利用控件序列化和反序列化,可以轻松实现控件的克隆。再比如在RunTime下得到控件的C#,VB等代码,因为序列化的中间产物就是C#,VB等代码,所以利用这个机制很容易实现。
今天我要介绍的是如何序列化System.Windows.Forms.Control这类复杂的对象。在介绍之前,我们先看看序列化和反序列化的流程。
分离出复杂的逻辑,最简单的逻辑只有两步,第一步-序列化过程:将内存中的对象数据用XML格式的数据表示。第二步-反序列化过程:构造一个新对象,并将XML格式表示的数据赋值给这个新的对象。这样,原始对象和新构造的对象会有同样的数据,即表示同一种状态。
经常使用VS2005,VS2008等IDE的开发人员,特别是做过Design Time开发的开发人员可能会注意到,这个过程其实和IDE自动生成控件代码的过程相似。
在使用IDE的过程中,我们在Design环境中给Form拖一个TextBox控件,IDE会在Design环境中创建一个TextBox控件,并生成相关C#或者VB等格式的代码。这个阶段叫设计时(DesignTime)。这个过程类似我们刚才讲到的第一步-序列化过程。而我们点击运行的时候,这些文本格式的代码又会被编译运行,根据编译的代码生成一个新TextBox控件在Form上显示。这个阶段叫运行时(RunTime)。这个过程就类似我们刚才讲到的第二步-反序列化过程。可以看出来,IDE在DesignTime下是将控件实例序化成C#或VB等代码。而在RunTime下是将代码编译运行,生成新的控件实例来保持DesignTime下设计的状态与RunTime下显示的状态一致。事实上,这也是序列化和反序列化的一种方式。
既然IDE的这个过程也是序列化和反序列化的方式,那么IDE是怎么实现复杂控件序列化和反序列化的呢?这可能需要另外一篇文章才能讲清楚,这里暂不研究。不过我们可以知道,IDE是有能力做这件事情的。我们也可以利用IDE的这种能力,来实现复杂控件的序列化和反序列化。
下面这段代码演示了如何利用IDE的这种能力序列化一个Object。
1
/// <summary>2
/// Saves the <see cref="T
bject"/> to the given stream.3
/// </summary>4
/// <param name="value">5
/// The <see cref="T
bject"/> will be serialized to save.6
/// </param>7
/// <param name="stream">8
/// The stream to which the <see cref="T
bject"/> will be serialized.9
/// </param>10
public static void SaveObject(object value, Stream stream)11
{12
if (stream == null)13
{14
throw new ArgumentNullException();15
}16

17
using (DesignSurface designSurface = new DesignSurface())18
{19
designSurface.BeginLoad(new DefaultCodeDomDesignerLoader());20
ComponentSerializationService serializationService = designSurface.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;21

22
SerializationStore store = serializationService.CreateStore();23
serializationService.Serialize(store, value);24
store.Save(stream);25
}26
}27

下面这段代码演示了如何利用IDE的这种能力反序列化一个Object。
1
/// <summary>2
/// Loads a <see cref="T
bject"/> from the given stream.3
/// </summary>4
/// <param name="stream">5
/// The stream from which to load the <see cref="T
bject"/>.6
/// </param>7
/// <returns>8
/// The loaded <see cref="T:Object"/>.9
/// </returns>10
public static object LoadObject(Stream stream)11
{12
if (stream == null)13
{14
throw new ArgumentNullException();15
}16

17
using (DesignSurface designSurface = new DesignSurface())18
{19
designSurface.BeginLoad(new DefaultCodeDomDesignerLoader());20
ComponentSerializationService serializationService = designSurface.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;21

22
SerializationStore store = serializationService.LoadStore(stream);23
ICollection collection = serializationService.Deserialize(store);24

25
foreach (IComponent associatedComponent in designSurface.ComponentContainer.Components)26
{27
designSurface.ComponentContainer.Remove(associatedComponent);28
}29

30
IEnumerator enumerator = collection.GetEnumerator();31
if (enumerator.MoveNext())32
{33
return enumerator.Current;34
}35

36
return null;37
}38
}利用IDE的这种能力,我们还可以做很多事情,比如深度克隆控件。有时候,我们需要克隆一个复杂的控件,然而办法有限,要么使用反射枚举控件所有字段赋值,要么给所有属性一个一个赋值。而属性之间的依赖关系,引用类型的属性如何处理等都是难题。利用控件序列化和反序列化,可以轻松实现控件的克隆。再比如在RunTime下得到控件的C#,VB等代码,因为序列化的中间产物就是C#,VB等代码,所以利用这个机制很容易实现。

添加至收藏夹