为何MemoryStream无Memory<byte>构造函数?非重新分配数组的替代方案
为何不存在new MemoryStream(Memory<byte> buffer)或new MemoryStream(ReadOnlyMemory<byte> buffer)构造函数?除了重新分配新字节数组之外,还有哪些解决方法?
我有一个用于解析的API(来自Bouncy Castle C#),定义如下:
var parsed = HeartbeatMessage.Parse(Stream stream);
目前我持有Memory<byte>类型的实际数据,期望能进行如下调用:
Memory<byte> buffer = .... // data var parsed = HeartbeatMessage.Parse(new MemoryStream(buffer));
一、为什么没有直接的Memory<byte>/ReadOnlyMemory<byte>构造函数?
MemoryStream的核心设计依赖连续托管字节数组的直接操作,而Memory<byte>/ReadOnlyMemory<byte>的底层存储非常灵活:它可以是堆外内存、数组切片后的非连续段,甚至是自定义内存实现。如果强行绑定这类非标准内存,MemoryStream内部的数组操作逻辑会彻底失效,这是两者设计目标不匹配导致的。
二、无需重新分配数组的解决方法
1. 提取底层数组段(适用于托管数组底层的Memory<byte>)
如果你的Memory<byte>是基于标准托管数组的,可以用MemoryMarshal.TryGetArray直接获取数组和偏移信息,再传入MemoryStream的重载构造函数,完全避免内存复制:
Memory<byte> buffer = ...; if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> segment)) { using var ms = new MemoryStream(segment.Array, segment.Offset, segment.Count); var parsed = HeartbeatMessage.Parse(ms); } else { // 处理非托管/非数组底层的特殊场景,此时可能仍需复制(但这类情况较少见) }
2. 自定义适配Memory<byte>的Stream实现
如果需要频繁处理非托管底层的Memory<byte>,可以实现一个轻量的Stream子类,直接包装Memory<byte>进行读写:
public class MemoryStreamWrapper : Stream { private readonly Memory<byte> _memory; private int _position; public MemoryStreamWrapper(Memory<byte> memory) { _memory = memory; } public override bool CanRead => true; public override bool CanSeek => true; public override bool CanWrite => false; // 可根据需求改为true并实现Write逻辑 public override long Length => _memory.Length; public override long Position { get => _position; set => _position = (int)Math.Clamp(value, 0, Length); } public override int Read(byte[] buffer, int offset, int count) { int bytesToRead = Math.Min(count, (int)(Length - Position)); _memory.Slice(_position, bytesToRead).CopyTo(buffer.AsMemory(offset)); _position += bytesToRead; return bytesToRead; } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; case SeekOrigin.End: Position = Length + offset; break; } return Position; } // 按需实现或抛出异常的方法 public override void Flush() { } public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); }
使用示例:
Memory<byte> buffer = ...; using var ms = new MemoryStreamWrapper(buffer); var parsed = HeartbeatMessage.Parse(ms);
这个方案适配所有Memory<byte>场景,全程无内存复制。
3. 查找Bouncy Castle的替代重载
部分Bouncy Castle的解析类会提供直接接受byte[]或ReadOnlySpan<byte>的重载,你可以检查HeartbeatMessage是否有类似方法:
// 假设存在该重载 var parsed = HeartbeatMessage.Parse(buffer.Span);
如果有这类重载,这是最简洁高效的解决方案。
内容的提问来源于stack exchange,提问作者Perfect28

