C# 接口IBufferWriter<T>学习理解

IBufferWriter<T>是同步缓冲写入的协定,实现这个接口就拥有一个输出接收器

我是最近研究Protobuf序列化时发现它有个传递IBufferWriter<T>的构造,使用者只需要自己实现一个IBufferWriter<T>,创建后传递给Protobuf-net的序列化函数,就能得到其序列化后的字节流

先实现一个IBufferWriter<T>

    /// <summary>
    /// 实现IBufferWriter<T>接口的写入器
    /// </summary>
    public class BufferWriter : IBufferWriter<byte> {
        /// <summary>
        /// 缓冲区
        /// </summary>
        byte[] buffer;
        /// <summary>
        /// 缓冲区中数据的个数
        /// </summary>
        int length;
        /// <summary>
        /// 初始容量
        /// </summary>
        readonly int capacity;
        /// <summary>
        /// 创建一个缓冲区写入器
        /// </summary>
        /// <param name="capacity"></param>
        public BufferWriter(int capacity) {
            this.capacity = capacity;
            buffer = new byte[capacity];
        }
        /// <summary>
        /// 重置长度,复用该对象
        /// </summary>
        public void Reset() {
            length = 0;
        }
        /// <summary>
        /// 得到缓冲区中的数据
        /// </summary>
        /// <returns></returns>
        public (byte[], int) GetBuffer() {
            return (buffer, length);
        }
        /// <summary>
        /// 实现接口,
        /// </summary>
        /// <param name="count"></param>
        public void Advance(int count) {
            length += count;
        }
        /// <summary>
        /// 当缓冲区长度不足时,扩容缓冲区
        /// </summary>
        /// <param name="sizeHint"></param>
        void TryExpand(int sizeHint) {
            if (sizeHint + length > buffer.Length) {
                Array.Resize(ref buffer, buffer.Length + capacity);
            }
        }
        /// <summary>
        /// 返回缓冲区指定长度的可操作块
        /// </summary>
        /// <param name="sizeHint"></param>
        /// <returns></returns>
        public Memory<byte> GetMemory(int sizeHint = 0) {
            TryExpand(sizeHint);
            return buffer.AsMemory(length, sizeHint);
        }
        /// <summary>
        /// 返回缓冲区指定长度的可操作块
        /// </summary>
        /// <param name="sizeHint"></param>
        /// <returns></returns>
        public Span<byte> GetSpan(int sizeHint = 0) {
            TryExpand(sizeHint);
            return buffer.AsSpan(length, sizeHint);
        }
    }
View Code

写了一段测试代码来理解它

        public async Task Test() {
            await Task.CompletedTask;
            BufferWriter bw = new BufferWriter(1024); //这是一个缓冲区写入器
            for (int i = 0; i < 3; i++) { //循环测试其复用性
                bw.Reset(); //重置缓冲区复用创建的写入器对象
                TestWrite(bw);
                (byte[] buffer, int length) = bw.GetBuffer(); //得到缓冲区数据
                Console.WriteLine($"{string.Join(",", buffer.Take(length))}"); //输出写入的数据
            }
        }
        /// <summary>
        /// 模拟向缓冲区中写数据
        /// </summary> 
        void TestWrite(BufferWriter bw) {
            byte[] bytes = BitConverter.GetBytes(1024); //第一条数据,int32
            //使用接口中方法写入数据
            Span<byte> span = bw.GetSpan(bytes.Length); //先从申请一个span
            bytes.CopyTo(span); //然后将数据写入其中
            bw.Advance(bytes.Length);  //通知写入器已写入数据的长度

            //使用IBufferWriter<T>的扩展方法能直接写入数据 
            bw.Write(BitConverter.GetBytes(4096));  //第二条数据
        }
    }

 测试在protobuf-net中使用IBufferWriter<T>

        public void Test() {
            List<User> users = new List<User> {
                new User() {
                    ID = 1,
                    Name = "张大山",
                    Gender = 1,
                    BirthDate = new DateTime(2000, 1, 1),
                    Remark = "123",
                },
                new User() {
                    ID = 2,
                    Name = "李思思",
                    Gender = 0,
                    BirthDate = new DateTime(2006, 6, 6),
                    Remark = "测试啊123",
                }
            };
            BufferWriter bufferWriter = new BufferWriter(1024 * 1024);
            foreach (var user in users) {
                bufferWriter.Reset();  //BufferWriter能复用,适合高频次的序列化,性能更高
                Serializer.Serialize(bufferWriter, user); //把IBufferWriter<T>的实现传递给ProtoBuf,这个第三方的类库会使用其作为缓冲区填充序列化的数据
                (byte[] buffer, int length) = bufferWriter.GetBuffer();
                Console.WriteLine("protobuf-net序列化后的字节流:" + BitConverter.ToString(buffer, 0, length).Replace("-", " "));
            }
        }
        [ProtoContract]
        class User {
            [ProtoMember(1)]
            public int ID { get; set; }
            [ProtoMember(2)]
            public string Name { get; set; }
            [ProtoMember(3)]
            public byte Gender { get; set; }
            [ProtoMember(4)]
            public DateTime BirthDate { get; set; }
            [ProtoMember(5)]
            public string Remark { get; set; }
        }

 

posted @ 2024-01-10 18:12  WmW  阅读(70)  评论(0编辑  收藏  举报