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

发布时间 2024-01-10 18:13:41作者: WmW

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));  //第二条数据
        }
    }