You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 11:48:10