继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化

慕森王
关注TA
已关注
手记 407
粉丝 107
获赞 550

二进制序列化可以方便快捷的将对象进行持久化或者网络传输,并且体积小、性能高,应用面甚至还要高于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                 else108                 {109                     throw new RPCPamarsException("ParamsSerializeUtil.Deserialize 未定义的类型:" + type.ToString());110                 }111 112             }113             return obj;114         }

View Code

        其他详细的代码可以查看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         }

View Code

        运行结果:

https://img.mukewang.com/5b2f219700010ee806700167.jpg 

原文出处


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP