ArraySegment是带偏移和长度的数组指针包装器,非切片工具或安全容器;常用于Socket.ReceiveAsync实现零拷贝,需确保offset和count合法,且池化数组须手动Return。

ArraySegment
ArraySegment 为什么常出现在 Socket.ReceiveAsync 参数里
因为网络 I/O 需要零拷贝传递缓冲区片段:接收方只关心刚读到的 bytesRead 字节,但底层 byte[] 缓冲区通常是固定大小(如 8192)。ArraySegment<byte></byte> 能把“有效数据范围”精准传给解析逻辑,不复制、不分配、不触发 GC。
- ✅ 正确用法:
var segment = new ArraySegment<byte>(buffer, 0, bytesRead)</byte>,且必须在bytesRead > 0后才构造 - ❌ 危险操作:
new ArraySegment<byte>(null, 0, 0)</byte>或new ArraySegment<byte>(buffer, 100, 1000)</byte>(越界不报错,但后续读写会崩) - ⚠️ 注意:
Socket.SendAsync(ArraySegment<byte>)</byte>会直接读取segment.Array的内存,若该数组来自ArrayPool<byte>.Shared.Rent()</byte>,你必须在发送完成后调用Return(),否则池化数组被重复使用会导致脏数据
不能直接 foreach 或用 [i] 访问 ArraySegment
ArraySegment<t></t> 实现了 IList<t></t>,但它的 GetEnumerator() 是显式实现,无法直接 foreach;也没有索引器,segment[i] 会编译失败。
- 要遍历:必须手动循环
for (int i = segment.Offset; i ,再通过 <code>segment.Array[i]访问 - 要取第 k 个元素:写成
segment.Array[segment.Offset + k],且必须自己校验k - 想用
foreach或[i]?升级到 .NET Core 2.1+ 后,优先转成segment.AsSpan(),再用span[i]或foreach (var b in span)
ArraySegment.Array 可能为 null,Offset 和 Count 不做越界检查
构造函数接受 null 数组,且不会抛异常:new ArraySegment<int>(null, 5, 10)</int> 合法,但之后访问 segment.Array 就是 NullReferenceException。更隐蔽的是:越界访问不报错 —— segment.Array[segment.Offset + segment.Count] 直接读堆垃圾或崩溃。
- 每次使用前必须判空:
if (segment.Array == null) throw new ArgumentException(...) - 所有基于
Offset和Count的计算,都要先确认segment.Offset + segment.Count - 不要依赖
segment.Count做边界防护 —— 它只是你当初传进去的数,没人校验它是否合法
ArraySegment 和 Span / Memory 混用时的兼容陷阱
很多人想“升级”老代码:把 ArraySegment<byte></byte> 转成 Span<byte></byte> 再用 Range 语法,但版本兼容性极敏感。
-
segment.AsSpan()在 .NET Standard 2.0 中不存在,仅 .NET Core 2.1+ / .NET 5+ 支持 - 旧框架只能手写:
new Span<byte>(segment.Array, segment.Offset, segment.Count)</byte>,且segment.Array必须非 null - 异步方法中传
ArraySegment<byte></byte>给Memory<byte></byte>参数?别硬转 ——new Memory<byte>(segment.Array, segment.Offset, segment.Count)</byte>才安全,且要注意Memory<t></t>的生命周期管理
最易被忽略的一点:ArraySegmentArrayPool 归还),后续任何访问都是未定义行为 —— 这类 bug 往往只在高并发压测时爆发,调试难度极高。


















