如何在C#中创建指定步长的Span?类似Unity的NativeSlice
实现指定步长的Span元素划分与类Unity NativeSlice功能
一、能否将256字节Span按步长32字节划分成8个20字节元素?
完全可以。256字节的内存中,按步长32字节偏移,每个元素取前20字节(剩余12字节忽略),刚好能得到8个有效元素(32*7 +20 =244 <256,最后一组剩余12字节自动忽略)。
二、具体实现代码
1. 直接划分单个元素的示例
// 假设已有256字节的内存缓冲区 byte[] buffer = new byte[256]; Span<byte> bufferSpan = buffer; const int ElementSize = 20; // 元素T的大小 const int Stride = 32; // 步长 const int ElementCount = 8; // 元素总数 for (int i = 0; i < ElementCount; i++) { int startOffset = i * Stride; // 截取对应位置的20字节作为元素内存 Span<byte> elementBytes = bufferSpan.Slice(startOffset, ElementSize); // 转换为目标类型T的Span(T必须是unmanaged类型) Span<My20ByteStruct> element = MemoryMarshal.Cast<byte, My20ByteStruct>(elementBytes); // 操作元素,比如赋值 element[0] = new My20ByteStruct { /* 初始化字段 */ }; }
2. 封装类Unity NativeSlice的通用实现
如果需要复用类似NativeSlice的步长访问能力,可以封装一个CustomNativeSlice<T>的ref struct(和Span一样,仅栈上可用,避免GC):
using System; using System.Runtime.InteropServices; public ref struct CustomNativeSlice<T> where T : unmanaged { private readonly Span<byte> _underlyingBuffer; private readonly int _stride; private readonly int _elementSize; public CustomNativeSlice(Span<byte> buffer, int stride) { _underlyingBuffer = buffer; _stride = stride; _elementSize = sizeof(T); if (_elementSize > _stride) throw new ArgumentOutOfRangeException(nameof(stride), "步长不能小于元素类型的大小"); } // 可访问的元素总数 public int Length => _underlyingBuffer.Length / _stride; // 索引器:直接返回元素的引用,支持读写 public ref T this[int index] { get { if (index < 0 || index >= Length) throw new ArgumentOutOfRangeException(nameof(index)); int elementStart = index * _stride; Span<byte> elementBytes = _underlyingBuffer.Slice(elementStart, _elementSize); return ref MemoryMarshal.GetReference(MemoryMarshal.Cast<byte, T>(elementBytes)); } } // 获取指定索引的元素Span(适合批量操作) public Span<T> GetElementSpan(int index) { if (index < 0 || index >= Length) throw new ArgumentOutOfRangeException(nameof(index)); int elementStart = index * _stride; return MemoryMarshal.Cast<byte, T>(_underlyingBuffer.Slice(elementStart, _elementSize)); } }
3. 针对你的场景使用示例
// 定义20字节的结构体(示例) public struct My20ByteStruct { public long Field1; // 8字节 public int Field2; // 4字节 public short Field3; // 2字节 public byte Field4; // 1字节 public byte Field5; // 1字节 // 合计20字节 } // 初始化256字节缓冲区 byte[] buffer = new byte[256]; Span<byte> bufferSpan = buffer; // 创建自定义NativeSlice,步长32字节 var slice = new CustomNativeSlice<My20ByteStruct>(bufferSpan, 32); // 访问并修改第0个元素 ref My20ByteStruct firstElement = ref slice[0]; firstElement.Field1 = 123456; // 遍历所有8个元素 for (int i = 0; i < slice.Length; i++) { ref My20ByteStruct elem = ref slice[i]; // 处理元素逻辑 }
三、关于步长等于元素大小的兼容场景
你提到的new Span(ptr, 64)(int类型,步长4字节)的情况,CustomNativeSlice<T>也能完美兼容:当步长等于sizeof(T)时,其行为和普通Span<T>完全一致,自动退化为连续数组的访问模式。
内容的提问来源于stack exchange,提问作者Sebastien Lachapelle
相关产品推荐
相关产品推荐

