BlockCopy一个类?获取"对象必须是一个基元数组"

时间:2015-10-17 01:59:52

标签: c# arrays memory

我有一个充满此类实例的数组

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#已经知道的类型转换为字节,这样的浮点数?但为什么?我的班级充满了原始类型......我觉得它应该知道如何做到这一点。我错过了什么?

3 个答案:

答案 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的基本数组不同,内存中没有其他类型的二进制标准。所以低级操作在一般类型上是不实际的(除了在某些情况下,按位复制可能导致一致性问题 - 即克隆FileStreamDrawing.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++;
        }