自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化
二进制序列化可以方便快捷的将对象进行持久化或者网络传输,并且体积小、性能高,应用面甚至还要高于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 }
反序列化实现如下:
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 。
自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化相关推荐
- C# dotnet 一个看上去还能用的二进制序列化帮助类
这仅是一个辅助方法帮助类,可以协助小伙伴写二进制序列化的效率,代码也还看的过去 在开始之前,我需要说明的是,如果不是必要,不要使用二进制序列化.因为很难做到版本兼容,如果写错了也不知道是哪里写错了,调 ...
- msdn安装后怎么用_Win10不好用?安装官方版精简Win10后,运行比win7更快更流畅
尽管不想承认,确实是大白菜的电脑配置有点旧了,导致从win7升级最新版win10系统后,电脑是100个不好用!!! 虽然,非常怀念使用win7系统时,那种操作的流畅度,那种运行的速度~ 但是,大白菜和 ...
- 安装更强大更美观的zsh,配置oh my zsh及插件
安装更强大更美观的zsh,配置oh my zsh及插件 #0x0 安装zsh #0x1 安装oh my zsh #0x2 配置zshrc #0x3 配置主题 #0x4 安装插件 #0x5 小结 #0x ...
- 使用现场总线更快更远
使用现场总线更快更远 Going faster and further with Fieldbus PROCENTEC等行业专家表示,基于RS-485的现场总线技术(PROFIBUS®)和工业以太网( ...
- Google 开源的依赖注入库,比 Spring 更小更快!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:GinoBeFunny zhuanlan.zhihu.com ...
- 苹果公司提出Mobile-ViT | 更小更轻精度更高,MobileNets或成为历史
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨ChaucerG 来源丨集智书童 MobileviT是一个用于移动设备的轻量级通用可视化Tran ...
- 更快更强,谷歌提出SWideRNet:全景分割新标杆来啦!
作者丨Happy 编辑丨极市平台 导读 本文是DeepLab系列作者"Liang-Chieh Chen"大神在全景分割领域的又一力作.它在Wide-ResNet的基础上引入SE与& ...
- 如何更好的创建Java对象
2019独角兽企业重金招聘Python工程师标准>>> 静态工厂 除了使用构造函数创建对象外,还可以使用静态工厂来创建对象,JDK中大量使用了这种技巧,例如: public stat ...
- 科宇扫地机器人_我的三年16台智能扫地机器人使用回忆录 篇四:扫地谁更精准更干净?新一代3D视讯+激光成像 PK 老式激光扫描,万字实测对比分享...
我的三年16台智能扫地机器人使用回忆录 篇四:扫地谁更精准更干净?新一代3D视讯+激光成像 PK 老式激光扫描,万字实测对比分享 2019-05-23 11:22:00 37点赞 166收藏 53评论 ...
最新文章
- 俄通信监管机构回应封锁微信: 收到资料核验后将解除封锁
- 示波器上mode选择开关上的norm和auto是什么意思?
- python为什么中文要encoding-python 中文编码问题如何解决?
- 1.5 matlab常量与变量
- 【Linux】20.shell脚本 检测是否 ping 通
- leetcode 258. 各位相加(Java版)
- linux lsof/netstat查看进程和端口号相关命令:
- linux 命令api,linux命令行下字典,使用有道API
- tar.gz及tar.bz2两种常见格式的打包压缩及解压方法
- java 多个timer_java – Timer正在创建多个计时器实例
- [转]win7-64位系统添加access的ODBC数据源 看不到其它数据源的问题
- 【C++】【GADL】读取栅格数据获取信息
- jupyter notebook 快捷键
- 曼联队选择Tezos作为官方区块链和训练工具包合作伙伴
- 计算机更新后无法远程,重装系统后无法进行远程桌面连接怎么办
- 集成第三方单点登录JIRA(Colfluence同理)
- Python基础——PyCharm版本——第八章、文件I/O(核心2——JSON序列化与反序列化——超重点)
- HDU1849 SG函数
- 蚂蚁金服Java后端(一面)面试题
- 疲劳载荷与S-N曲线