C#中如何不复制数据获取数组子段并转为byte[]?
问题解决思路与方案
首先明确:不存在直接生成指向原数组片段的byte[]实例的方法——因为.NET中的数组是固定大小的连续内存块,每个byte[]对象都对应独立的内存区域,任何通过ToArray()生成的新数组必然是原数据的副本。
要实现「修改片段元素时原数组同步变更」的需求,你可以使用以下几种.NET内置的内存包装类型,它们完全复用原数组的内存,不会产生数据复制:
1. ArraySegment(全.NET版本兼容)
这是最经典的解决方案,本质是对原数组的偏移和长度进行包装:
byte[] test = { 0xA, 0xB, 0xC, 0xD }; // 参数:原数组、起始偏移量、片段长度 ArraySegment<byte> segment = new ArraySegment<byte>(test, 1, 2); // 修改片段元素,原数组同步更新 segment[0] = 0xBB; // 此时test[1]的值已变为0xBB
它支持数组式的索引访问,所有操作都直接作用于原数组内存。
2. Memory/Span(.NET Core 2.1+/NET 5+)
这两个是现代.NET引入的内存抽象类型,功能更灵活,性能更优:
byte[] test = { 0xA, 0xB, 0xC, 0xD }; // Memory<byte> 支持异步场景,可安全跨线程传递 Memory<byte> memorySlice = test.AsMemory(1, 2); memorySlice.Span[0] = 0xCC; // 原数组test[1]同步变为0xCC // Span<byte> 是栈分配的内存视图,同步场景下性能最高 Span<byte> spanSlice = test.AsSpan(1, 2); spanSlice[1] = 0xDD; // 原数组test[2]同步变为0xDD
关于LINQ的说明
你用test.Skip(1).Take(2)得到的IEnumerable<byte>是延迟执行的枚举器,它只会在遍历的时候访问原数组的对应元素,但无法将其转为不复制数据的byte[]——因为ToArray()的核心逻辑就是遍历枚举器,把元素复制到新分配的数组中,必然产生副本。
自定义包装类(按需使用)
如果需要一个更贴近byte[]使用体验的类型,可以自己封装一个简单的视图类:
public class ByteSlice { private readonly byte[] _sourceArray; private readonly int _startIndex; public int Length { get; } public ByteSlice(byte[] source, int startIndex, int length) { _sourceArray = source; _startIndex = startIndex; Length = length; } public byte this[int index] { get { if (index < 0 || index >= Length) throw new IndexOutOfRangeException(); return _sourceArray[_startIndex + index]; } set { if (index < 0 || index >= Length) throw new IndexOutOfRangeException(); _sourceArray[_startIndex + index] = value; } } } // 使用示例 byte[] test = { 0xA, 0xB, 0xC, 0xD }; ByteSlice slice = new ByteSlice(test, 1, 2); slice[0] = 0xEE; // test[1] 现在的值为0xEE
内容的提问来源于stack exchange,提问作者yo3hcv
相关产品推荐
相关产品推荐

