二进制序列化可以方便快捷的将对象进行持久化或者网络传输,并且体积小、性能高,应用面甚至还要高于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=;
  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 = ;
  6.  
  7. byte[] data = null;
  8.  
  9. if (param == null)
  10. {
  11. len = ;
  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. if (type.IsGenericType || type.IsArray)
  82. {
  83. if (TypeHelper.DicTypeStrs.Contains(type.Name))
  84. data = SerializeDic((System.Collections.IDictionary)param);
  85. else if (TypeHelper.ListTypeStrs.Contains(type.Name) || type.IsArray)
  86. data = SerializeList((System.Collections.IEnumerable)param);
  87. else
  88. data = SerializeClass(param, type);
  89. }
  90. else if (type.IsClass)
  91. {
  92. data = SerializeClass(param, type);
  93. }
  94.  
  95. }
  96. if (data != null)
  97. len = data.Length;
  98. }
  99. datas.AddRange(BitConverter.GetBytes(len));
  100. if (len > )
  101. {
  102. datas.AddRange(data);
  103. }
  104. return datas.Count == ? null : datas.ToArray();
  105. }

反序列化实现如下:

  1. public static object Deserialize(Type type, byte[] datas, ref int offset)
  2. {
  3. dynamic obj = null;
  4.  
  5. var len = ;
  6.  
  7. byte[] data = null;
  8.  
  9. len = BitConverter.ToInt32(datas, offset);
  10. offset += ;
  11. if (len > )
  12. {
  13. data = new byte[len];
  14. Buffer.BlockCopy(datas, offset, data, , 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, ));
  28. }
  29. else if (type == typeof(short))
  30. {
  31. obj = (BitConverter.ToInt16(data, ));
  32. }
  33. else if (type == typeof(int))
  34. {
  35. obj = (BitConverter.ToInt32(data, ));
  36. }
  37. else if (type == typeof(long))
  38. {
  39. obj = (BitConverter.ToInt64(data, ));
  40. }
  41. else if (type == typeof(float))
  42. {
  43. obj = (BitConverter.ToSingle(data, ));
  44. }
  45. else if (type == typeof(double))
  46. {
  47. obj = (BitConverter.ToDouble(data, ));
  48. }
  49. else if (type == typeof(decimal))
  50. {
  51. obj = (BitConverter.ToDouble(data, ));
  52. }
  53. else if (type == typeof(DateTime))
  54. {
  55. var dstr = Encoding.UTF8.GetString(data);
  56. var ticks = long.Parse(dstr.Substring());
  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[]);
  66. }
  67. else if (numType == typeof(short))
  68. {
  69. obj = Enum.ToObject(type, BitConverter.ToInt16(data, ));
  70. }
  71. else if (numType == typeof(int))
  72. {
  73. obj = Enum.ToObject(type, BitConverter.ToInt32(data, ));
  74. }
  75. else
  76. {
  77. obj = Enum.ToObject(type, BitConverter.ToInt64(data, ));
  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. }

其他详细的代码可以查看https://github.com/yswenli/SAEA/blob/master/Src/SAEA.RPC/Serialize/ParamsSerializeUtil.cs

功能基本实现了,下面对比一下10000次的实体序列化与反序列化测试结果:

实体代码:

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

运行结果:

更多内容,请Fork或Star我的github:https://github.com/yswenli/SAEA

C#高性能二进制序列化的更多相关文章

  1. 开源!一款功能强大的高性能二进制序列化器Bssom.Net

    好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...

  2. 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转

    线程安全使用(四)   这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...

  3. REST RPC HTTP vs 高性能二进制协议 序列化和通信协议

    edisonchou https://mp.weixin.qq.com/s/-XZXqXawR-NxJMPCeiNsmg .NET Core微服务之服务间的调用方式(REST and RPC) Edi ...

  4. 性能超四倍的高性能.NET二进制序列化库

    二进制序列化在.NET中有很多使用场景,如我们使用分布式缓存时,通常将缓存对象序列化为二进制数据进行缓存,在ASP.NET中,很多中间件(如认证等)也都是用了二进制序列化. 在.NET中我们通常使用S ...

  5. 二进制序列化框架easypack发布啦!

    简介 easypack是基于boost.serialization的二进制序列化框架,使用极其方便. Examples 基本类型 int age = 20; std::string name = &q ...

  6. C# 序列化(二)二进制序列化的案例

    这篇是针对上一篇讲序列化的文章的一个实际案例,WinForm程序的主界面如下:

  7. C#中的二进制序列化和Json序列化

    序列化就是把一个对象变成流的形式,方便传输和还原.小弟不才,总结下对二进制序列化和Json序列化的使用: 1.首先,二进制序列化(BinaryFormatter)要求要序列化的类必须是可序列化的(即在 ...

  8. java编解码技术,json序列化与二进制序列化

    1.何为json序列化与二进制序列化 通常我们在程序中采用的以json为传输,将json转为对象的就是json序列化了.而二进制序列化通常是我们将数据转换为二进制进行传输,然后在进行各类转换操作 2. ...

  9. .net下二进制序列化的格式分析[转]

    .net下二进制序列化的格式分析[转] -- 综合应用 (http://www.Host01.Com/article/Net/00020003/) --- .net下二进制序列化的格式分析 (http ...

随机推荐

  1. 17.Odoo产品分析 (二) – 商业板块(10) – 电子商务(1)

    查看Odoo产品分析系列--目录 安装电子商务模块 1. 主页 点击"商店"菜单:  2. 添加商品 在odoo中,你不用进入"销售"模块,再进入产品列表添加产 ...

  2. 13.Odoo产品分析 (二) – 商业板块(6) –采购(3)

    接上一篇  查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (二) – 商业板块(6) –采购(2) 7. 仓库 仓库是在安装采购管理模块时出现的菜单.用于管理工厂库存,包括已经在手的货物 ...

  3. MySQL 慢查询日志配置与简析

    MySQL慢查询日志配置与简析 By:授客 QQ:1033553122   <1> 查看是否开启慢查询日志 SHOW VARIABLES LIKE 'slow%'; 说明: a. 如果sl ...

  4. Spark操作parquet文件

    package code.parquet import java.net.URI import org.apache.hadoop.conf.Configuration import org.apac ...

  5. C#面向对象 1

    using System; using System.Collections.Generic; using System.Collections; using System.Linq; using S ...

  6. Unity Editor 下创建Lua和Text文件

    预览 在Project视图中,扩展右键菜单,右键 – Create - Text File 创建一个Text文件,或者Lua文件. 关键点 获取当前选择的路径,以Assets路径开头 var sele ...

  7. Win10安装sqlserver2014打开显示黑色界面,mardown打开显示报错

    问题描述: 我电脑从win7更新到win10以后就打开sqlserver2014显示黑色背景有问题,卸载了又装都是没有用 然后我又发现mardown也是有问题打开报告什么错误,忘记截图了,去网上找了个 ...

  8. spark SQL读取ORC文件从Driver启动到开始执行Task(或stage)间隔时间太长(计算Partition时间太长)且产出orc单个文件中stripe个数太多问题解决方案

    1.背景: 控制上游文件个数每天7000个,每个文件大小小于256M,50亿条+,orc格式.查看每个文件的stripe个数,500个左右,查询命令:hdfs fsck viewfs://hadoop ...

  9. Eclipse debug 断点不能调试 ,Eclipse Unable to install breakpoint in 解决办法

    解决:[1]项目工程名 ,右键 --> properties --> java compiler -->class file Generation 位置  Add line numb ...

  10. (转)Spring Boot(十八):使用 Spring Boot 集成 FastDFS

    http://www.ityouknow.com/springboot/2018/01/16/spring-boot-fastdfs.html 上篇文章介绍了如何使用 Spring Boot 上传文件 ...