【发布时间】:2022-01-15 10:36:25
【问题描述】:
我正在使用 C# (.NET 5)。想象一下,我有一个存储结构数组(比如浮点数)的类:
public class StoresArray
{
private float[] floats;
}
这个类的数据是从一个序列化的二进制文件中加载的。为了分配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>、Marshal 和MemoryMarshal 关联的函数也同样失败。当然,我可以使用 spans(连同其他方法,如 BitConverter 或不安全指针)从 byte[] 构建 new float[],但这会导致额外的数组分配,如以及转换字节的附加操作。在我提出的问题(动态加载视频游戏资产)的上下文中,我想尽可能优化性能。
在现代 C# 中,是否可以在不产生额外分配的情况下重新解释和存储结构数组?
【问题讨论】:
-
这取决于您要序列化的格式。
-
您尝试了
Marshal类的哪些功能,它是如何“失败”的? -
@dan04 在此上下文 (imo) 中最值得注意的
Marshal函数是PtrToStructure,它确实让我成功地创建了 one 结构 (T item = Marshal.PtrToStructure<T>(new IntPtr(address))。不幸的是,它并没有让我像我希望的那样重新解释数组。 -
嗨!我觉得这个问题过早地结束了(就像很多人一样)。尽管链接的问题从根本上归结为相同的答案(不,您不能在 C# 中重新解释强制转换数组),但该问题是在五年前提出的,甚至在
Span<T>存在之前。此外,我从另一个可能对其他人有价值的问题空间中处理了这个问题(“如何重新解释转换数组?”)。最后,Matthew Watson 下面的回答给出了另一个问题中没有的重要见解(将T[]直接传递给输入流)。 -
@Boann 在读取大型基元数组(例如双精度数)时绝对不是这种情况。常规(旧式)方法将让您使用
BitConverter将每个double转换为字节数组,以便从流中读取/写入流。我在 BenchmarkDotNet 上的时间表明,使用Span<T>和MemoryMarshal.AsBytes()在写入和读取MemoryStream时快五倍以上。
标签: c# arrays reinterpret-cast