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); } }
写了一段测试代码来理解它
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; } }