感谢zhoufoxcn 和 zzxap 的帖子。http://topic.csdn.net/u/20090327/10/2559468d-9fbf-4caa-944e-d992f9609ba9.html
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。例 如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。反之,反序列化根据流重新构造对象。此外还可以将对象序列化后保存到本地,再次运行的时候可以从本地文件中“恢复”对象 到序列化之前的状态。 在.net中有提供了几种序列化的方式: 二进制序列化 XML序列化 SOAP序列化
C# code
一个例子,以显示如何正确的运用SerializableAttribute属性和NonSerializedAttribute属性。该程序中运用到了
XML格式器,不过同时给出了二进制格式器为参考(程序中将其用
"
//
"
标注),其实现的结果是一样的。该程序实现
的功能是在序列化和反序列化操作前后测试对象因包含了[NonSerialized()]的字段而显示不同的屏幕打印结果。其
代码如下:
using
System;
using
System.IO;
using
System.Runtime.Serialization;
using
System.Runtime.Serialization.Formatters.Soap;
//
using System.Runtime.Serialization.Formatters.Binary;
public
class
Test {
public
static
void
Main() {
//
创建一个新的测试对象
TestSimpleObject obj
=
new
TestSimpleObject(); Console.WriteLine(
"
Before serialization the object contains:
"
); obj.Print();
//
创建一个文件"data.XML"并将对象序列化后存储在其中
Stream stream
=
File.Open(
"
data.xml
"
, FileMode.Create); SoapFormatter formatter
=
new
SoapFormatter();
//
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, obj); stream.Close();
//
将对象置空
obj
=
null
;
//
打开文件"data.xml"并进行反序列化得到对象
stream
=
File.Open(
"
data.xml
"
, FileMode.Open); formatter
=
new
SoapFormatter();
//
formatter = new BinaryFormatter();
obj
=
(TestSimpleObject)formatter.Deserialize(stream); stream.Close(); Console.WriteLine(
""
); Console.WriteLine(
"
After deserialization the object contains:
"
); obj.Print(); } }
//
一个要被序列化的测试对象的类
[Serializable()]
public
class
TestSimpleObject {
public
int
member1;
public
string
member2;
public
string
member3;
public
double
member4;
//
标记该字段为不可被序列化的
[NonSerialized()]
public
string
member5;
public
TestSimpleObject() { member1
=
11
; member2
=
"
hello
"
; member3
=
"
hello
"
; member4
=
3.14159265
; member5
=
"
hello world!
"
; }
public
void
Print() { Console.WriteLine(
"
member1 = '{0}'
"
, member1); Console.WriteLine(
"
member2 = '{0}'
"
, member2); Console.WriteLine(
"
member3 = '{0}'
"
, member3); Console.WriteLine(
"
member4 = '{0}'
"
, member4); Console.WriteLine(
"
member5 = '{0}'
"
, member5); } } 三.基本序列化(Basic Serialization) vs 自定义序列化(Custom Serialization):
.net框架为我们提供了两种方式的序列化:一种为基本序列化、另一种为自定义序列化。值得注意的是,序列化的
方式和前面提到的序列化的格式是不同的概念。序列化的方式是指.Net框架将程序的数据转化为能被存储并传输的格
式的实际过程,它是不管程序员运用了何种类型的格式器的(二进制格式器还是XML格式器)。而序列化的格式则指
程序的数据是被转化成二进制格式了还是被转化成XML格式了。 完成序列化的最简单的方法便是让.Net框架自动为我们完成整个过程,而我们不必去管它内部是如何具体实现的,
这种方法便是前面提到的"基本序列化"。在这种方式下,我们需要做的仅仅是将类标记上[Serializable()]属性。然后
.Net框架便调用该类的对象并将它转化为所需的格式。同时你还可以控制其中的某些字段不被序列化,方法就是前面
所述的将该字段标记上[NonSerialized()]属性。这样,最最简单和基本的序列化工作就完成了,不过其内部是如何
实现的你是不得而知的,同时你也不能进一步控制序列化过程的程序行为。 如果你要获得对序列化的更大的控制权,那么你就得使用"自定义序列化"的方式。通过使用这种方式,你可以完
全的控制类的哪些部分能被序列化而哪些部分不能,同时你还可以控制如何具体的进行序列化。运用该方式的好处就
是能克服基本序列化所会遇到的问题。我们在运用基本序列化将一个类的对象序列化完毕并存储在文件中后,假设该
对象原来有三个字段,如果此时该对象增加了一个字段,那么再将该对象从文件中反序列化出来时会发生字段数不一
致的错误。这样的问题是基本序列化所不能解决的,只能运用自定义序列化的方式来解决。 在介绍自定义序列化之前,我先给出介绍过程中所要用到的实例程序的代码。这是一个时间安排程序,其中要用
到将不同的时间格式进行转化的操作。所以运用序列化的机制能很好的解决这个问题。
using
System;
using
System.Runtime.Serialization;
namespace
SerializationSample { [Serializable()]
public
class
Schedule {
protected
System.DateTime start;
protected
System.DateTime end;
//
每个时间间隔所要增加的毫秒数
protected
long
interval;
public
System.DateTime Start {
get
{
return
start;}
set
{start
=
value;}}
public
System.DateTime End {
get
{
return
end;}
set
{end
=
value;}}
public
long
Interval {
get
{
return
interval;}
set
{interval
=
value;}}
public
Schedule(System.DateTime Start, System.DateTime End,
long
Interval) { start
=
Start; end
=
End; interval
=
Interval; }
//
如果已经到了结束的时间,则返回结束时间,否则返回下一次运行的时间
public
System.DateTime NextRunTime {
get
{ System.TimeSpan ts
=
new
System.TimeSpan(end.Ticks
-
System.DateTime.Now.Ticks);
if
(ts.Milliseconds
>
0
) {
return
System.DateTime.Now.AddMilliseconds(interval); }
else
{
return
end; } } } } } 自定义序列化: 下面我就向大家介绍自定义序列化以及反序列化的具体过程。首先,程序的类必须实现
System.Runtime.Serialization.ISerializable接口,该接口的功能就是允许对象控制其自己的序列化和反序列化
过程。所以我们得重新定义上面的类: [Serializable()]
public
class
ScheduleCustom : System.Runtime.Serialization.Iserializable 接下来,我们必须对该接口调用GetObjectData()的实现,也即我们必须在上面的类中给出
GetObjectData()的具体实现。其函数原型如下:
void
GetObjectData(SerializationInfo info, StreamingContext context); 上面的类中GetObjectData()的具体实现如下:
public
void
GetObjectData(SerializationInfo info,StreamingContext context) {
//
运用info对象来添加你所需要序列化的项
info.AddValue(
"
start
"
, start); info.AddValue(
"
end
"
, end); info.AddValue(
"
interval
"
, interval); } 然而对于这么一个简单的方法,读者可能不能理会到系列化带来的强大功能,所以下面我就给这个方法添加一些
东西。如果在系列化过程中我们要查看类型为DateTime的
"
start
"
属性的输出的话,其结果会是.Net框架默认的格式:
2002
-
12
-
19T14:
09
:
13.3457440
-
07
:
00
而对于没有.Net框架的用户,或是在其他时间区域内的用户而言,这么一个格式的时间可能是非常难以理解的,
所以我们有必要将时间的格式转化为格林威治标准时间格式,于是修改GetObjectData()方法如下:
public
void
GetObjectData(SerializationInfo info,StreamingContext context) {
//
运用info对象来添加你所需要序列化的项
//
同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式
info.AddValue(
"
start
"
, System.TimeZone.CurrentTimeZone.ToUniversalTime(start)); info.AddValue(
"
end
"
, System.TimeZone.CurrentTimeZone.ToUniversalTime(end)); info.AddValue(
"
interval
"
, interval); info.AddValue(
"
timeformat
"
,
"
utc
"
); } 这样一来,我们在系列化过程中查看
"
start
"
属性时就会得到如下结果:
8
/
19
/
2002
9
:
09
:
13
PM 同时请注意我们在GetObjectData()方法中添加的一个名为
"
timeformat
"
的额外属性,通过它我们可以方便
的知道系列化过程中所使用的时间格式。如果有兴趣的话,你还可以从System.Globalization.DateTimeFormatInfo
这个名字空间中获取更多有关时间格式的信息。 自定义反序列化: 你可以通过调用一个自定义的构造函数来完成自定义反序列化的操作。该构造函数的定义如下:
public
ScheduleCustom (SerializationInfo info,StreamingContext context); 在上面的类中,我们的ScheduleCustom()方法将完成把时间格式从格林威治标准时间格式反序列化为当地时
间的格式的操作,其函数实现如下:
public
ScheduleCustom (SerializationInfo info,StreamingContext context) {
this
.start
=
info.GetDateTime(
"
start
"
).ToLocalTime();
this
.end
=
info.GetDateTime(
"
end
"
).ToLocalTime();
this
.interval
=
info.GetInt32(
"
interval
"
); } 在完成自定义序列化和自定义反序列化后,我们的时间安排程序变成了如下形式:
using
System;
using
System.Runtime.Serialization;
namespace
SerializationSample { [Serializable()]
public
class
ScheduleCustom : System.Runtime.Serialization.ISerializable {
protected
System.DateTime start;
protected
System.DateTime end;
//
每个时间间隔所要增加的毫秒数
protected
long
interval;
public
System.DateTime Start {
get
{
return
start;}
set
{start
=
value;}}
public
System.DateTime End {
get
{
return
end;}
set
{end
=
value;}}
public
long
Interval {
get
{
return
interval;}
set
{interval
=
value;}}
public
ScheduleCustom(System.DateTime Start, System.DateTime End,
long
Interval) { start
=
Start; end
=
End; interval
=
Interval; }
//
如果已经到了结束的时间,则返回结束时间,否则返回下一次运行的时间
public
System.DateTime NextRunTime {
get
{ System.TimeSpan ts
=
new
System.TimeSpan(end.Ticks
-
System.DateTime.Now.Ticks);
if
(ts.Milliseconds
>
0
) {
return
System.DateTime.Now.AddMilliseconds(interval); }
else
{
return
end; } } }
public
void
GetObjectData(SerializationInfo info,StreamingContext context) {
//
运用info对象来添加你所需要序列化的项
//
同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式
info.AddValue(
"
start
"
, System.TimeZone.CurrentTimeZone.ToUniversalTime(start)); info.AddValue(
"
end
"
, System.TimeZone.CurrentTimeZone.ToUniversalTime(end)); info.AddValue(
"
interval
"
, interval); info.AddValue(
"
timeformat
"
,
"
utc
"
); }
public
ScheduleCustom (SerializationInfo info,StreamingContext context) {
this
.start
=
info.GetDateTime(
"
start
"
).ToLocalTime();
this
.end
=
info.GetDateTime(
"
end
"
).ToLocalTime();
this
.interval
=
info.GetInt32(
"
interval
"
); } } }