我有一个充满此类实例的数组
public class TransformData {
public float pos_x, pos_y, pos_z;
public float rot_x, rot_y, rot_z;
public float scale_x, scale_y, scale_z;
}
我尝试使用以下方法将此数组转换为字节:
byte[] byteArray = new byte[transformArrayByteSizeHere];
uffer.BlockCopy(transformArrayHere, 0, byteArray, 0, transformArrayByteSizeHere);
这样我就可以写一个二进制文件了。但是我得到了:"对象必须是一个基元数组。"我收集这意味着我只能从C#已经知道的类型转换为字节,这样的浮点数?但为什么?我的班级充满了原始类型......我觉得它应该知道如何做到这一点。我错过了什么?
答案 0 :(得分:4)
您应该注意的一件事是引用类型数组(在C#中class
关键字表示引用类型)甚至不连续。有一个缓冲区连续保存所有指针,但内容本身在GC堆中的每个区域都是分散的。
更改为结构(值类型)可以解决这个问题,实际上,您可以使用p / invoke或C#原始指针进行blit。但是Buffer.BlockCopy
不够聪明,不能看到用户定义的结构由基元组成,因此可以复制,它只是拒绝所有复合类型。因此,您必须在使用不安全代码的批量复制之间做出决定,或者手动逐个元素地逐个进行复制。
需要unsafe
关键字的快速方法大致为:
// assumes TransformData is struct, not class
fixed( TransformData* p = &transformArray[0] )
Marshal.Copy(new IntPtr(p), byteArray, 0, byteArray.Length);
答案 1 :(得分:1)
与int / byte / float的基本数组不同,内存中没有其他类型的二进制标准。所以低级操作在一般类型上是不实际的(除了在某些情况下,按位复制可能导致一致性问题 - 即克隆FileStream
或Drawing.Pen
对象而不更新内部计数器将导致非常坏问题)。
通常最好使用定义良好的序列化格式(XML / JSON,如果文本很好)来存储数据。如果你需要更多紧凑的二进制格式,许多对象Binary Serialization就可以工作。如果你需要兼容非.Net系统,像proto-buff这样的东西可能是更好的选择(参见When should I use XML Serialization vs. Binary Serialization in the .NET framework?进行讨论)。
在所有序列化的情况下,您需要注意由于存在运行时数据(即SqlConnection
或OS等连接/服务类型,存在大量无法序列化/反序列化的类型级别类型 - 文件,绘图对象。)
如果你真的想要复制字节 - 将你的类型限制为某些struct
可能适用于Marshl.Copy
- 请参阅How to convert a structure to a byte array in C#?。
答案 2 :(得分:0)
我认为最好的方法是将类更改为结构并尽可能使用Array.Copy。
但要注意" Array.Copy"虽然用于复制到同一个数组中但实现得很糟糕且非常慢(比C ++慢两倍)。 (截至2017-06-01)
这是我调用extern C代码的代码的一部分:
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false), SuppressUnmanagedCodeSecurity]
public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
// ************************************************************************
/// <summary>
/// Insert an "item" at "index" position into an "array" which could be bigger
/// than the real count of items ("countOfValidItems") in it.
/// </summary>
/// <param name="array"></param>
/// <param name="item"></param>
/// <param name="index"></param>
/// <param name="countOfValidItems"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
unsafe public static void InsertItemPinvokeC(Point[] array, Point item, int index, ref int countOfValidItems)
{
if (countOfValidItems >= array.Length)
{
Point[] dest = new Point[array.Length * 2];
// The next 2 calls are replaced by a call to CopyMemory (memcopy from defined in C library "msvcrt.dll").
// Array.Copy(array, 0, dest, 0, index);
// Array.Copy(array, index, dest, index + 1, countOfValidItems - index);
//Buffer.BlockCopy(array, 0, dest, 0, (countOfValidItems - index) * sizeof(Point));
//Buffer.BlockCopy(array, index * sizeof(Point), dest, (index + 1) * sizeof(Point), (countOfValidItems - index) * sizeof(Point));
fixed (Point* s = array)
{
fixed (Point* d = dest)
{
CopyMemory(d, s, (ulong)(index * sizeof(Point)));
CopyMemory(d + ((index + 1) * sizeof(Point)), s + (index * sizeof(Point)), (ulong)((countOfValidItems - index) * sizeof(Point)));
}
}
array = dest;
}
else
{
// Array.Copy(array, index, array, index + 1, countOfValidItems - index);
// Buffer.BlockCopy(array, index * sizeof(Point), array, (index + 1) * sizeof(Point), (countOfValidItems - index) * sizeof(Point));
fixed (Point* p = array)
{
CopyMemory(p + ((index + 1) * sizeof(Point)), p + (index * sizeof(Point)), (ulong)((countOfValidItems - index) * sizeof(Point)));
}
}
array[index] = item;
countOfValidItems++;
}