---
id: ipackage
title: 包序列化模式
---

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

## 二、特点
【优点】

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

【缺点】

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

## 三、使用
【实体类】
```csharp
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();
    }
}
```
【打包和解包】
```csharp
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倍。

![](@site/static/img/docs/ipackage-1.png)
