在 C# 中,如何将 byte[] 重新解释为 T[],其中 T 是一个结构?

In C#, how can I reinterpret byte[] as T[], where T is a struct?

我正在使用 C# (.NET 5)。假设我有一个 class 存储结构数组(例如,浮点数):

public class StoresArray
{
    private float[] floats;
}

此 class 的数据是从序列化二进制文件加载的。为了分配 floats 数组,我使用辅助函数从序列化文件中读取字节。重要的是,此函数然后尝试 重新解释 加载的字节 直接作为 float[] 而不是复制到新数组。

public static class Deserializer
{
    public static float[] Load(string file)
    {
        byte[] bytes = LoadBytesFromFile(file);

        // This is a compiler error, of course.
        return (float[])bytes;
    }
}

预期用途如下:

// Within the StoresArray class...
floats = Deserializer.Load("MyFile.file");

这里值得注意的是,我试图 float[] 存储为成员变量 ,而不仅仅是迭代在 byte[] 本地。因此,通过 Span<T> (Span<float> floatSpan = MemoryMarshal.Cast<byte, float>(bytes.AsSpan())) 进行转换是不够的。与 Memory<T>MarshalMemoryMarshal 关联的函数也同样失败。当然,我可以使用跨度(连同其他方法,如 BitConverter 或不安全指针)从 byte[] 构建一个 new float[],但是这将导致额外的数组分配,以及转换字节的额外操作。在我询问的上下文中(动态加载视频游戏资产),我想尽可能地优化性能。

在现代 C# 中,是否可以重新解释和存储 结构数组而不产生额外的分配?

要写作你可以这样做:

public static void WriteArrayToStream<T>(Stream output, T[] array) where T: unmanaged
{
    var span = array.AsSpan();
    var bytes = MemoryMarshal.AsBytes(span);
    output.Write(bytes);
}

为了阅读,你可以这样做:

public static (T[] Result, int Count) ReadArrayFromStream<T>(Stream input, int n) where T: unmanaged
{
    T[] result = new T[n];
    var span   = result.AsSpan();
    var bytes  = MemoryMarshal.AsBytes(span);
    int count  = input.Read(bytes);

    return (result, count/Marshal.SizeOf<T>());
}

请注意,它 returns 是一个元组,因为如果没有足够的数据可用,只有前 Count 个元素会有有效数据。

下面是一个显示写入和读取 double[] 数组的示例:

MemoryStream mem = new MemoryStream();
double[] data = Enumerable.Range(0, 100).Select(x => (double)x).ToArray();
WriteArrayToStream(mem, data);
Console.WriteLine(mem.Length); // 800

mem.Position       = 0;
var (array, count) = ReadArrayFromStream<double>(mem, 200);
Console.WriteLine(count); // 100
Console.WriteLine(array[42]);  // 42