在06年开发基于Window CE的嵌入式组态软件时就曾遇到序列化问题,由于程序运行在Window CE平台,其上的.Net Compact Framework仅支持XML序列化,而XML序列化不仅序列化后的体积较大,而且执行时间较长(参见我以前写的文章:嵌入式组态环境升级及XML反序列化慢的困惑、如何加速XML反序列化(精简框架集2.0SP1,WinCE4.2))。
而当时支持.Net Compact Framework的二进制序列化就是CompactFormatter(参见黎波的文章: 在.NET Compact Framework 2.0 中使用序列化)了,由于是第三方所开发,功能上尚不完善,故没有选用。
前段时间看MSN Direct代码,发现使用.Net Micro Framework二进制序列化后的广播数据比较小,并且速度快。所以想办法把相关代码做了平台移植,可没有想到的是在.net micro Framework和.Net Framework中都可以正常运行的代码,在.NET Compact Framework中竟然不能运行(主要是对Assembly操作的相关函数支持不够)。
由于目前在.Net Compact Framework开发的应用逐渐增多,并且最近也打算升级原先开发的嵌入式组态软件,经过再三考虑决定自行开发支持精简框架集的二进制序列化(说明:.Net Micro Framework平台上的二进制序列化,由于运行在ARM系列的CPU上,会考虑一些大小端的问题,所以多于一个byte的值变量都要进行特殊处理,速度相对较慢,不过.Net Micro Framework二进制序列化的优点是,支持bit序列化(bool变量按位存取,也可以为其它变量指定位数),所以它的序列化结果是最精简的)。
有.Net Micro Framework二进制序列化代码做参考,所以自行开发一个支持精简框架集二进制序列化库,并不是一件特别繁杂和痛苦的事:-)
在开发二进制序列化之前,对要完成的二进制序列化库,有以下几方面的考虑:
一、速度要快;
二、体积要小;
三、要支持自定义序列化;
针对第一点,故舍弃了.Net Micro Framework二进制序列化的bit序列化支持,并且精简了一些功能,比如仅支持原生数据类型的一维数组序列化,仅支持ArrayList,不支持泛型,此外不自行反射Assembly中的Type,和.Net Compact Framework 的XML序列化一样,需要开发者从外部传入Type列表;
针对第二点采用了很多.Net Micro Framework的二进制序列化思想,如序列化后的数据中不保存Type 的完整的名字,仅保存该名字的4个字节的哈希值,字符串的长度和数组长度用变长的1~4个字节的空间来保存,多个对象引用相同,仅保存首个对象等等;
而第三点主要和我开发的嵌入式组态功能相关,大量的图元派生于基类图元,而基类中的大量属性,在不同的图元中用到的都不同,如果一概而论全部序列化,则结果会比较大,而采用自定义序列化就能很好地解决这个问题。此外值得一提的是.Net Micro Framework二进制序列化和.Net Compact Framework XML序列化都不支持该功能。
用了我大约4天的时间,终于完成了.Net Compact Framework 二进制序列化的第一版V0.1,目前测试的结果还是令人满意的(以下结果是在windows平台下测试的,循环执行100次)。
1、.Net Micro Framework binary serialize
Data Length : 103 byte
Serialize Time : 46 ms
Deserialize Time : 46 ms
2、.Net Compact Framework xml serialize
Data Length : 998 byte
Serialize Time : 31545 ms
Deserialize Time : 34092 ms
3、CompactFormatterPlus binary serialize
Data Length : 1598 byte
Serialize Time : 103 ms
Deserialize Time : 132 ms
4、.Net Framework binary serialize
Data Length : 828 byte
Serialize Time : 18 ms
Deserialize Time : 17 ms
5、Yefan binary serialize
Data Length : 113 byte
Serialize Time : 8 ms
Deserialize Time : 8 ms
由以上可以看出,除了在体积上稍稍大于.Net Micro Framework的二进制序列化外,和其它序列化后的结果相比,几乎相差一个数量级,此外执行时间是最小的,并且其它相比,是几个数量级的差别。
在开发二进制序列化过程中发现,.Net Compact Framework xml和CompactFormatterPlus都不支持循环引用,如下面的类:
Class Test1
{
Public int v1=0;
Public Object o=null;
}
Test1 t=new Test1();
t.o=t; //为自身
如果对t序列化,则.Net Compact Framework xml和CompactFormatterPlus都会出现异常,此外对CompactFormatterPlus,如果enum类型的基础类型不是默认的int型,也会抛出异常,如下面的枚举:
Enum Test:byte {one ,two};
主要测试代码如下:
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Text;
using
System.Windows.Forms;
using
System.Collections;
using
System.IO;
using
System.Xml.Serialization;
namespace
CETest
{
public
partial
class
frmTest : Form
{
public
frmTest()
{
InitializeComponent();
}
private
void
btnTest_Click(
object
sender, EventArgs e)
{
#region
类型定义
Type[] Types
=
new
Type[] {
typeof
(TestClass),
typeof
(TestClass1),
typeof
(TestClass2),
typeof
(Color) };
TestClass t1
=
new
TestClass();
t1.v1
=
11
;
t1.v2
=
22
;
t1.v3
=
"
33
"
;
t1.v4[
1
]
=
44
;
t1.v5[
0
]
=
"
55
"
;
t1.v6
=
TestClass.enumtest.enum2;
t1.V7
=
77
;
t1.v8.v1
=
88
;
t1.v9
=
new
TestClass2();
t1.v9.v1
=
99
;
t1.v10
=
t1.v9;
//
t1 循环引用
TestClass1 t11
=
new
TestClass1();
//
t11.v2.Add(t1);
//
t1 循环引用
t11.v2.Add(
3
);
t11.v2.Add(
5
);
t11.v1_base
=
123
;
TestClass2 t22
=
new
TestClass2();
t1.v11.Add(t11);
t1.v11.Add(t22);
t1.v11.Add(
1
);
//
t1.v12 = Color.Green;
#endregion
string
strInfo
=
""
;
Application.DoEvents();
long
start
=
0
;
double
tk1
=
0
, tk2
=
0
;
byte
[] bytData
=
null
;
int
Count
=
1
;
//
if (chkXML.Checked)
{
start
=
DateTime.Now.Ticks;
for
(
int
i
=
0
; i
<
Count; i
++
)
{
MemoryStream ms
=
new
MemoryStream();
XmlSerializer xmls
=
new
XmlSerializer(
typeof
(TestClass), Types);
xmls.Serialize(ms, t1);
bytData
=
ms.ToArray();
ms.Close();
}
tk1
=
TimeSpan.FromTicks(DateTime.Now.Ticks
-
start).TotalMilliseconds;
start
=
DateTime.Now.Ticks;
for
(
int
i
=
0
; i
<
Count; i
++
)
{
MemoryStream ms
=
new
MemoryStream(bytData);
XmlSerializer xmls
=
new
XmlSerializer(
typeof
(TestClass), Types);
TestClass obj2
=
(TestClass)xmls.Deserialize(ms);
ms.Close();
}
tk2
=
TimeSpan.FromTicks(DateTime.Now.Ticks
-
start).TotalMilliseconds;
strInfo
+=
ShowInfo(
"
.Net Compact Framework xml serialize
"
, bytData.Length, tk1, tk2);
}
//
if (chkCF.Checked)
//
{
//
start = DateTime.Now.Ticks;
//
for (int i = 0; i < Count; i++)
//
{
//
MemoryStream ms = new MemoryStream();
//
CompactFormatter.CompactFormatter cf = new CompactFormatter.CompactFormatter();
//
cf.Serialize(ms, t1);
//
bytData = ms.ToArray();
//
ms.Close();
//
}
//
tk1 = TimeSpan.FromTicks(DateTime.Now.Ticks - start).TotalMilliseconds;
//
start = DateTime.Now.Ticks;
//
for (int i = 0; i < Count; i++)
//
{
//
MemoryStream ms = new MemoryStream(bytData);
//
CompactFormatter.CompactFormatter cf = new CompactFormatter.CompactFormatter();
//
TestClass obj3 = (TestClass)cf.Deserialize(ms);
//
ms.Close();
//
}
//
tk2 = TimeSpan.FromTicks(DateTime.Now.Ticks - start).TotalMilliseconds;
//
strInfo += ShowInfo("CompactFormatterPlus binary serialize", bytData.Length, tk1, tk2);
//
}
//
if (chkYFSoft.Checked)
{
start
=
DateTime.Now.Ticks;
for
(
int
i
=
0
; i
<
Count; i
++
)
{
MemoryStream ms
=
new
MemoryStream();
YFSoft.BinaryFormatter bf2
=
new
YFSoft.BinaryFormatter(Types);
bf2.Serialize(ms, t1);
bytData
=
ms.ToArray();
ms.Close();
}
tk1
=
TimeSpan.FromTicks(DateTime.Now.Ticks
-
start).TotalMilliseconds;
start
=
DateTime.Now.Ticks;
for
(
int
i
=
0
; i
<
Count; i
++
)
{
MemoryStream ms
=
new
MemoryStream(bytData);
YFSoft.BinaryFormatter bf2
=
new
YFSoft.BinaryFormatter(Types);
TestClass obj4
=
(TestClass)bf2.Deserialize(ms);
ms.Close();
}
tk2
=
TimeSpan.FromTicks(DateTime.Now.Ticks
-
start).TotalMilliseconds;
strInfo
+=
ShowInfo(
"
Yefan binary serialize
"
, bytData.Length, tk1, tk2);
}
txtInfo.Text
=
strInfo;
}
private
string
ShowInfo(
string
title,
int
length,
double
milliseconds1,
double
milliseconds2)
{
string
strInfo
=
""
;
strInfo
+=
title
+
"
\r\n
"
;
strInfo
+=
"
Data Length :
"
+
length.ToString()
+
"
\r\n
"
;
strInfo
+=
"
Serialize Time :
"
+
milliseconds1.ToString()
+
"
ms\r\n
"
;
strInfo
+=
"
Deserialize Time :
"
+
milliseconds2.ToString()
+
"
ms\r\n\r\n
"
;
return
strInfo;
}
}
[Serializable]
public
class
TestClassBase
{
public
int
v1_base
=
111
;
public
string
v2_base
=
"
222
"
;
}
[Serializable]
public
class
TestClass1 : TestClassBase
{
public
int
v1
=
1
;
public
ArrayList v2
=
new
ArrayList();
}
[Serializable]
public
class
TestClass2
{
public
byte
v1
=
1
;
public
string
v2
=
"
2
"
;
}
[Serializable]
public
class
TestClass
//
:YFSoft.ISerializable
{
public
int
v1
=
1
;
[NonSerialized]
public
long
v2
=
2
;
public
string
v3
=
"
v3
"
;
public
int
[] v4
=
new
int
[
3
] {
0
,
1
,
2
};
public
string
[] v5
=
new
string
[
2
] {
"
123
"
,
"
456
"
};
public
enum
enumtest :
int
{ enum0, enum1, enum2 };
//
:byte
public
enumtest v6
=
enumtest.enum1;
private
UInt16 v7;
public
UInt16 V7 {
get
{
return
v7; }
set
{ v7
=
value; } }
public
TestClass1 v8
=
new
TestClass1();
public
TestClass2 v9
=
null
;
//
new TestClass2();
public
object
v10
=
null
;
public
ArrayList v11
=
new
ArrayList();
//
public Color v12 = Color.Red;
#region
ISerializable 成员
//
public void GetObjectData(YFSoft.SerializationInfo si)
//
{
//
v1 = si.GetInt32();
//
}
//
public void SetObjectData(YFSoft.SerializationInfo si)
//
{
//
si.AddValue(v1);
//
}
#endregion
}
}
下载地址:
http://www.sky-walker.com.cn/yefan/YFSerializeTest.rar