如果无法访问Memory<T>
,最终选择了选项(2),但不需要编组,只需强制转换:fixed
在 an 中使用字节数组unsafe struct
并强制转换为/从这些中转换,如下所示:
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class TestStructWithFixed : MonoBehaviour
{
public const int MAX = 5;
public const int SIZEOF_ELEMENT = 8;
public struct Element
{
public uint x;
public uint y;
//8 bytes
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct Container
{
public int id; //4 bytes
public unsafe fixed byte bytes[MAX * SIZEOF_ELEMENT];
}
public Container container;
void Start ()
{
Debug.Log("SizeOf container="+Marshal.SizeOf(container));
Debug.Log("SizeOf element ="+Marshal.SizeOf(new Element()));
unsafe
{
Element* elements;
fixed (byte* bytes = container.bytes)
{
elements = (Element*) bytes;
//show zeroed bytes first...
for (int i = 0; i < MAX; i++)
Debug.Log("i="+i+":"+elements[i].x);
//low order bytes of Element.x are at 0, 8, 16, 24, 32 respectively for the 5 Elements
bytes[0 * SIZEOF_ELEMENT] = 4;
bytes[4 * SIZEOF_ELEMENT] = 7;
}
elements[2].x = 99;
//show modified bytes as part of Element...
for (int i = 0; i < MAX; i++)
Debug.Log("i="+i+":"+elements[i].x); //shows 4, 99, 7 at [0], [2], [4] respectively
}
}
}
unsafe
访问速度非常快,并且没有编组或副本 - 这正是我想要的。
如果可能为您的所有成员使用 4 字节int
s 或float
s struct
,您甚至可以更好地将fixed
缓冲区基于这种类型(uint
始终是一个干净的选择) - 易于调试。
2021 年更新
今年我重新审视了这个主题,在 Unity 5 中进行原型设计(由于快速编译/迭代时间)。
坚持使用一个非常大的字节数组并在托管代码中使用它会更容易,而不是使用fixed
+ unsafe
(顺便说一下,从 C# 7.3开始,不再需要fixed
每次都使用关键字来固定固定大小的缓冲区为了访问它)。
随着fixed
我们失去类型安全;这是互操作数据的一个自然缺点——无论是原生数据还是托管数据的互操作;CPU和GPU;或 Unity 主线程代码和用于新 Burst / Jobs 系统的代码之间。这同样适用于托管字节缓冲区。
因此,更容易接受使用无类型托管缓冲区并自己编写偏移量 + 大小。fixed
/unsafe
提供(一点)更多便利,但不是很多,因为您同样必须指定编译时结构字段偏移量并在每次数据设计更改时更改这些。至少使用托管 VLA,我可以对代码中的偏移量求和,但这确实意味着这些不是编译时常量,因此会失去一些优化。
fixed
与托管 VLA(在 Unity 中)相比,以这种方式分配缓冲区的唯一真正好处是,对于后者,GC 有可能在播放过程中将您的整个数据模型移动到其他地方,这可能会导致打嗝,虽然我还没有看到这在生产中有多严重。
但是,Burst 不直接支持托管阵列。