Skip to main content

包序列化模式

一、说明

包序列化模式是为了解决极限序列化的问题。常规序列化的瓶颈,主要是反射、表达式树、创建对象等几个方面,这几个问题在运行时阶段,都没有一个好的解决方案。目前在net6以后,微软大力支持源代码生成,这使得这类问题得到了很大程度的解决。但是对于老项目,或者无法使用net6和vs2022以上的项目,是无法使用的。所以,这时候包序列化模式就显得非常需要了。

二、特点

【优点】

  1. 简单、可靠、高效
  2. 可以支持所有类型(需要自己编写代码)
  3. 数据量最少(从理论来说这是占数据量最轻量的设计)

【缺点】

  1. 要求序列化端和反序列化端必须保持一致,可以存在数据差异,但是不能出现数据断层。

三、使用

【实体类】

class MyClass : IPackage
{
public int P1 { get; set; }
public string P2 { get; set; }
public char P3 { get; set; }
public double P4 { get; set; }
public List<int> P5 { get; set; }
public Dictionary<int, MyClassModel> P6 { get; set; }
public void Package(ByteBlock byteBlock)
{
//基础类型直接写入。
byteBlock.Write(P1);
byteBlock.Write(P2);
byteBlock.Write(P3);
byteBlock.Write(P4);

//集合类型,可以先判断是否为null
byteBlock.WriteIsNull(P5);
if (P5 != null)
{
//如果不为null
//就先写入集合长度
//然后遍历将每个项写入
byteBlock.Write(P5.Count);
foreach (var item in P5)
{
byteBlock.Write(item);
}
}

//字典类型,可以先判断是否为null
byteBlock.WriteIsNull(P6);
if (P6 != null)
{
//如果不为null
//就先写入字典长度
//然后遍历将每个项,按键、值写入
byteBlock.Write(P6.Count);
foreach (var item in P6)
{
byteBlock.Write(item.Key);
byteBlock.WritePackage(item.Value);//因为值MyClassModel实现了IPackage,所以可以直接写入
}
}
}

public void Unpackage(ByteBlock byteBlock)
{
//基础类型按序读取。
this.P1 = byteBlock.ReadInt32();
this.P2 = byteBlock.ReadString();
this.P3 = byteBlock.ReadChar();
this.P4 = byteBlock.ReadDouble();

var isNull = byteBlock.ReadIsNull();
if (!isNull)
{
int count = byteBlock.ReadInt32();
var list = new List<int>(count);
for (int i = 0; i < count; i++)
{
list.Add(byteBlock.ReadInt32());
}
this.P5 = list;
}


isNull = byteBlock.ReadIsNull();//复用前面的变量,省的重新声明
if (!isNull)
{
int count = byteBlock.ReadInt32();
var dic = new Dictionary<int, MyClassModel>(count);
for (int i = 0; i < count; i++)
{
dic.Add(byteBlock.ReadInt32(), byteBlock.ReadPackage<MyClassModel>());
}
this.P6 = dic;
}
}
}

class MyClassModel : PackageBase
{
public DateTime P1 { get; set; }
public override void Package(ByteBlock byteBlock)
{
byteBlock.Write(P1);
}

public override void Unpackage(ByteBlock byteBlock)
{
this.P1 = byteBlock.ReadDateTime();
}
}

【打包和解包】

var myClass = new MyClass();
myClass.P1 = 1;
myClass.P2 = "若汝棋茗";
myClass.P3 = 'a';
myClass.P4= 3;

myClass.P5=new List<int> { 1, 2, 3 };

myClass.P6= new Dictionary<int, MyClassModel>()
{
{ 1,new MyClassModel(){ P1=DateTime.Now} },
{ 2,new MyClassModel(){ P1=DateTime.Now} }
};

using (ByteBlock byteBlock=new ByteBlock())
{
myClass.Package(byteBlock);//打包,相当于序列化

byteBlock.Seek(0);//将流位置重置为0

var myNewClass = new MyClass();
myNewClass.Unpackage(byteBlock);//解包,相当于反序列化
}

四、性能评测

基准测试表明:

包序列化模式和使用源代码生成方式工作的MemoryPack几乎一样。比json方式快了10倍多,比微软的json快了近4倍。