二進制序列化可以友善快捷的将對象進行持久化或者網絡傳輸,并且體積小、性能高,應用面甚至還要高于json的序列化;開始之前,先來看看dotcore/dotne自帶的二進制序列化:C#中對象序列化和反序列化一般是通過BinaryFormatter類來實作的二進制序列化、反序列化的。
BinaryFormatter序列化:
1 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
2
3 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
4
5 serializer.Serialize(memStream, request);
BinaryFormatter反序列化:
1 memStream.Position=0;
2
3 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer =
4
5 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
6
7 object newobj = deserializer.Deserialize(memStream);
8
9 memStream.Close();
10
11 return newobj;
用着多了就發現BinaryFormatter有很多地方不妥,下面就來數數這個序列化的“三宗罪”:
1.類名上面要加上[Serializable],不加不給序列化;正常的用法應該是序列化一個對象,不需的地方加上NonSerialized才合理吧;
2.序列化byte[]結果非常大,使用System.Text.Encoding.UTF8.GetString(bytes)檢視下,發現裡面有一大堆的中繼資料;對比看看google的protobuf,pb為什麼在網絡上應用的越來越多,這和他本身序列化完後體積小有着絕大部門的原因;
3.序列化對象需要完全一緻,連類的命名空間都要相同,這點對于分面式開發的應用來說也是不可接受的;
既然BinaryFormatter不好用,那就隻能動手自行實作一個解決上述問題的二進制序列化方案;首先去掉[Serializable]這個标簽,接着主要是分析對象,并定義對象序列化後的資料結構;這裡的想法是按長度加内容的方式來定義,舉個例子:使用int作為長度,來儲存一個int值,序列化完應該是:4,0,0,0,1,0,0,0這樣的一組bytes,同理可以将int、short、long、float、double、datetime、enum、array、string、class、generic等按照這個格式進行序列化,這裡主要使用的是BitConverter、反射等來實作序列化與反序列化;
序列化實作如下:

1 public static byte[] Serialize(object param)
2 {
3 List<byte> datas = new List<byte>();
4
5 var len = 0;
6
7 byte[] data = null;
8
9 if (param == null)
10 {
11 len = 0;
12 }
13 else
14 {
15 if (param is string)
16 {
17 data = Encoding.UTF8.GetBytes((string)param);
18 }
19 else if (param is byte)
20 {
21 data = new byte[] { (byte)param };
22 }
23 else if (param is bool)
24 {
25 data = BitConverter.GetBytes((bool)param);
26 }
27 else if (param is short)
28 {
29 data = BitConverter.GetBytes((short)param);
30 }
31 else if (param is int)
32 {
33 data = BitConverter.GetBytes((int)param);
34 }
35 else if (param is long)
36 {
37 data = BitConverter.GetBytes((long)param);
38 }
39 else if (param is float)
40 {
41 data = BitConverter.GetBytes((float)param);
42 }
43 else if (param is double)
44 {
45 data = BitConverter.GetBytes((double)param);
46 }
47 else if (param is DateTime)
48 {
49 var str = "wl" + ((DateTime)param).Ticks;
50 data = Encoding.UTF8.GetBytes(str);
51 }
52 else if (param is Enum)
53 {
54 var enumValType = Enum.GetUnderlyingType(param.GetType());
55
56 if (enumValType == typeof(byte))
57 {
58 data = new byte[] { (byte)param };
59 }
60 else if (enumValType == typeof(short))
61 {
62 data = BitConverter.GetBytes((Int16)param);
63 }
64 else if (enumValType == typeof(int))
65 {
66 data = BitConverter.GetBytes((Int32)param);
67 }
68 else
69 {
70 data = BitConverter.GetBytes((Int64)param);
71 }
72 }
73 else if (param is byte[])
74 {
75 data = (byte[])param;
76 }
77 else
78 {
79 var type = param.GetType();
80
81
82 if (type.IsGenericType || type.IsArray)
83 {
84 if (TypeHelper.DicTypeStrs.Contains(type.Name))
85 data = SerializeDic((System.Collections.IDictionary)param);
86 else if (TypeHelper.ListTypeStrs.Contains(type.Name) || type.IsArray)
87 data = SerializeList((System.Collections.IEnumerable)param);
88 else
89 data = SerializeClass(param, type);
90 }
91 else if (type.IsClass)
92 {
93 data = SerializeClass(param, type);
94 }
95
96 }
97 if (data != null)
98 len = data.Length;
99 }
100 datas.AddRange(BitConverter.GetBytes(len));
101 if (len > 0)
102 {
103 datas.AddRange(data);
104 }
105 return datas.Count == 0 ? null : datas.ToArray();
106 }
View Code
反序列化實作如下:

1 public static object Deserialize(Type type, byte[] datas, ref int offset)
2 {
3 dynamic obj = null;
4
5 var len = 0;
6
7 byte[] data = null;
8
9 len = BitConverter.ToInt32(datas, offset);
10 offset += 4;
11 if (len > 0)
12 {
13 data = new byte[len];
14 Buffer.BlockCopy(datas, offset, data, 0, len);
15 offset += len;
16
17 if (type == typeof(string))
18 {
19 obj = Encoding.UTF8.GetString(data);
20 }
21 else if (type == typeof(byte))
22 {
23 obj = (data);
24 }
25 else if (type == typeof(bool))
26 {
27 obj = (BitConverter.ToBoolean(data, 0));
28 }
29 else if (type == typeof(short))
30 {
31 obj = (BitConverter.ToInt16(data, 0));
32 }
33 else if (type == typeof(int))
34 {
35 obj = (BitConverter.ToInt32(data, 0));
36 }
37 else if (type == typeof(long))
38 {
39 obj = (BitConverter.ToInt64(data, 0));
40 }
41 else if (type == typeof(float))
42 {
43 obj = (BitConverter.ToSingle(data, 0));
44 }
45 else if (type == typeof(double))
46 {
47 obj = (BitConverter.ToDouble(data, 0));
48 }
49 else if (type == typeof(decimal))
50 {
51 obj = (BitConverter.ToDouble(data, 0));
52 }
53 else if (type == typeof(DateTime))
54 {
55 var dstr = Encoding.UTF8.GetString(data);
56 var ticks = long.Parse(dstr.Substring(2));
57 obj = (new DateTime(ticks));
58 }
59 else if (type.BaseType == typeof(Enum))
60 {
61 var numType = Enum.GetUnderlyingType(type);
62
63 if (numType == typeof(byte))
64 {
65 obj = Enum.ToObject(type, data[0]);
66 }
67 else if (numType == typeof(short))
68 {
69 obj = Enum.ToObject(type, BitConverter.ToInt16(data, 0));
70 }
71 else if (numType == typeof(int))
72 {
73 obj = Enum.ToObject(type, BitConverter.ToInt32(data, 0));
74 }
75 else
76 {
77 obj = Enum.ToObject(type, BitConverter.ToInt64(data, 0));
78 }
79 }
80 else if (type == typeof(byte[]))
81 {
82 obj = (byte[])data;
83 }
84 else if (type.IsGenericType)
85 {
86 if (TypeHelper.ListTypeStrs.Contains(type.Name))
87 {
88 obj = DeserializeList(type, data);
89 }
90 else if (TypeHelper.DicTypeStrs.Contains(type.Name))
91 {
92 obj = DeserializeDic(type, data);
93 }
94 else
95 {
96 obj = DeserializeClass(type, data);
97 }
98 }
99 else if (type.IsClass)
100 {
101 obj = DeserializeClass(type, data);
102 }
103 else if (type.IsArray)
104 {
105 obj = DeserializeArray(type, data);
106 }
107 else
108 {
109 throw new RPCPamarsException("ParamsSerializeUtil.Deserialize 未定義的類型:" + type.ToString());
110 }
111
112 }
113 return obj;
114 }
其他詳細的代碼可以檢視
ParamsSerializeUtil.cs功能基本實作了,下面對比一下10000次的實體序列化與反序列化測試結果:
實體代碼:
1 var groupInfo = new GroupInfo()
2 {
3 GroupID = 1,
4 IsTemporary = false,
5 Name = "yswenli group",
6 Created = DateTimeHelper.Now,
7 Creator = new UserInfo()
8 {
9
10 ID = 1,
11 Birthday = DateTimeHelper.Now.AddYears(-100),
12 UserName = "yswenli"
13 },
14 Users = new System.Collections.Generic.List<UserInfo>()
15 {
16 new UserInfo()
17 {
18
19 ID = 1,
20 Birthday = DateTimeHelper.Now.AddYears(-100),
21 UserName = "yswenli"
22 }
23 }
24 };
測試代碼:

1 public static byte[] SerializeBinary(object request)
2 {
3
4 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =
5
6 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
7
8 using (System.IO.MemoryStream memStream = new System.IO.MemoryStream())
9 {
10 serializer.Serialize(memStream, request);
11
12 return memStream.ToArray();
13 }
14 }
15
16
17 public static object DeSerializeBinary(byte[] data)
18 {
19 using (System.IO.MemoryStream memStream = new System.IO.MemoryStream(data))
20 {
21 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer =
22
23 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
24
25 return deserializer.Deserialize(memStream);
26 }
27 }
28
29 static void SerializeTest()
30 {
31 var groupInfo = new GroupInfo()
32 {
33 GroupID = 1,
34 IsTemporary = false,
35 Name = "yswenli group",
36 Created = DateTimeHelper.Now,
37 Creator = new UserInfo()
38 {
39
40 ID = 1,
41 Birthday = DateTimeHelper.Now.AddYears(-100),
42 UserName = "yswenli"
43 },
44 Users = new System.Collections.Generic.List<UserInfo>()
45 {
46 new UserInfo()
47 {
48
49 ID = 1,
50 Birthday = DateTimeHelper.Now.AddYears(-100),
51 UserName = "yswenli"
52 }
53 }
54 };
55
56 var count = 100000;
57 var len1 = 0;
58 var len2 = 0;
59
60 Stopwatch sw = new Stopwatch();
61 sw.Start();
62
63 List<byte[]> list = new List<byte[]>();
64 for (int i = 0; i < count; i++)
65 {
66 var bytes = SerializeBinary(groupInfo);
67 len1 = bytes.Length;
68 list.Add(bytes);
69 }
70 ConsoleHelper.WriteLine($"BinaryFormatter實體序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
71
72 sw.Restart();
73 for (int i = 0; i < count; i++)
74 {
75 var obj = DeSerializeBinary(list[i]);
76 }
77 ConsoleHelper.WriteLine($"BinaryFormatter實體反序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
78 ConsoleHelper.WriteLine($"BinaryFormatter序列化生成bytes大小:{len1 * count * 1.0 / 1024 / 1024} Mb");
79 list.Clear();
80 sw.Restart();
81
82 for (int i = 0; i < count; i++)
83 {
84 var bytes = RPC.Serialize.ParamsSerializeUtil.Serialize(groupInfo);
85 len2 = bytes.Length;
86 list.Add(bytes);
87 }
88 ConsoleHelper.WriteLine($"ParamsSerializeUtil實體序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
89 sw.Restart();
90 for (int i = 0; i < count; i++)
91 {
92 int os = 0;
93
94 var obj = RPC.Serialize.ParamsSerializeUtil.Deserialize(groupInfo.GetType(), list[i], ref os);
95 }
96 ConsoleHelper.WriteLine($"ParamsSerializeUtil實體反序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
97 ConsoleHelper.WriteLine($"ParamsSerializeUtil序列化生成bytes大小:{len2 * count * 1.0 / 1024 / 1024} Mb");
98 sw.Stop();
99 }
運作結果:
感謝您的閱讀,如果您對我的部落格所講述的内容有興趣,請繼續關注我的後續部落格,我是yswenli 。