C# - 将一个字节数组转换为struct数组,反之亦然(反向)

时间:2013-07-24 17:06:40

标签: c# arrays casting struct byte

我想将Color []保存到文件中。为此,我发现使用“System.IO.File.WriteAllBytes”将字节数组保存到文件应该非常有效。

我想将我的Color [](struct数组)转换为字节数组,以安全的方式考虑:

  • 小端/大端的潜在问题(我认为不可能发生,但想确定)
  • 有2个不同的指针指向同一个内存,指向不同的类型。垃圾收集器是否知道该怎么做 - 移动对象 - 删除指针???

如果可能的话,有一种通用的方法来将任何结构的数组(T struct)转换为数组,反之亦然。

如果不可能,为什么?

谢谢, 埃里克

我认为这两个解决方案制作了我想避免的副本,并且它们都使用Marshal.PtrToStructure,这是特定于结构而不是结构数组:

5 个答案:

答案 0 :(得分:2)

关于数组类型转换

C#作为一种语言故意使得将对象或数组扁平化为字节数组的过程变得困难,因为这种方法违背了.NET强类型的原则。传统的替代方案包括一些通常被认为更安全,更健壮的序列化工具,或者手动序列化编码,例如BinaryWriter

如果可以隐式或显式地转换变量的类型,则只能执行两个不同类型的变量指向内存中的同一对象。从一个元素类型的数组转换到另一个元素类型并非易事:它必须转换跟踪数组长度等内容的内部成员。

写入和读取Color []到文件

的简单方法
void WriteColorsToFile(string path, Color[] colors)
{
    BinaryWriter writer = new BinaryWriter(File.OpenWrite(path));

    writer.Write(colors.Length);

    foreach(Color color in colors)
    {
        writer.Write(color.ToArgb());
    }

    writer.Close();
}

Color[] ReadColorsFromFile(string path)
{
    BinaryReader reader = new BinaryReader(File.OpenRead(path));

    int length = reader.ReadInt32();

    Colors[] result = new Colors[length];

    for(int n=0; n<length; n++)
    {
        result[n] = Color.FromArgb(reader.ReadInt32());
    }

    reader.Close();
}

答案 1 :(得分:1)

你可以使用指针,如果你真的想要复制每个字节而没有副本但是同一个对象,类似于:

var structPtr = (byte*)&yourStruct;
var size = sizeof(YourType);
var memory = new byte[size];
fixed(byte* memoryPtr = memory)
{
    for(int i = 0; i < size; i++)
    {
        *(memoryPtr + i) = *structPtr++;
    }
}
File.WriteAllBytes(path, memory);

我刚刚对此进行了测试,在添加fixed块并进行了一些小修改后,它看起来工作正常。

这是我用来测试的:

public static void Main(string[] args)
{
    var a = new s { i = 1, j = 2 };
    var sPtr = (byte*)&a;
    var size = sizeof(s);
    var mem = new byte[size];
    fixed (byte* memPtr = mem)
    {
        for (int i = 0; i < size; i++)
        {
            *(memPtr + i) = *sPtr++;
        }
    }
    File.WriteAllBytes("A:\\file.txt", mem);
}

struct s
{
    internal int i;

    internal int j;
}

结果如下:

example

(我手动解析了第二行中的十六进制字节,只有第一行是由程序生成的)

答案 2 :(得分:1)

自.NET Core 2.1开始,是的,我们可以!输入MemoryMarshal

我们会将Color[]视为ReadOnlySpan<Color>。我们将其重新解释为ReadOnlySpan<byte>。最后,由于WriteAllBytes没有基于跨度的重载,因此我们使用FileStream将跨度写入磁盘。

var byteSpan = MemoryMarshal.AsBytes(colorArray.AsSpan());
fileStream.Write(byteSpan);

作为一个有趣的旁注,您也可以将[StructLayout(LayoutKind.Explicit)]作为字段的属性进行试验。它允许您指定重叠的字段,从而有效地实现了联合的概念。

Here是MSDN上的一篇博客文章,对此进行了说明。它显示以下代码:

[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
    [FieldOffset(0)]
    public UInt16 myInt;

    [FieldOffset(0)]
    public Byte byte1;

    [FieldOffset(1)]
    public Byte byte2;
}

在此示例中,UInt16字段与两个Byte字段重叠。

这似乎与您要执行的操作密切相关。除了可以高效地写入所有字节(尤其是多个Color对象)的部分之外,它非常接近您。 :)

答案 3 :(得分:0)

工作代码供参考(注意,我不需要alpha通道):

// ************************************************************************
// If someday Microsoft make Color serializable ...
    //public static void SaveColors(Color[] colors, string path)
    //{
    //  BinaryFormatter bf = new BinaryFormatter();
    //  MemoryStream ms = new MemoryStream();
    //  bf.Serialize(ms, colors);
    //  byte[] bytes = ms.ToArray();
    //  File.WriteAllBytes(path, bytes);
    //}

// If someday Microsoft make Color serializable ...
    //public static Colors[] LoadColors(string path)
    //{
    //  Byte[] bytes = File.ReadAllBytes(path);
    //  BinaryFormatter bf = new BinaryFormatter();
    //  MemoryStream ms2 = new MemoryStream(bytes);
    //  return (Colors[])bf.Deserialize(ms2);
    //}

    // ******************************************************************
    public static void SaveColorsToFile(Color[] colors, string path)
    {
        var formatter = new BinaryFormatter();

        int count = colors.Length;

        using (var stream = File.OpenWrite(path))
        {
            formatter.Serialize(stream, count);

            for (int index = 0; index < count; index++)
            {
                formatter.Serialize(stream, colors[index].R);
                formatter.Serialize(stream, colors[index].G);
                formatter.Serialize(stream, colors[index].B);
            }
        }
    }

    // ******************************************************************
    public static Color[] LoadColorsFromFile(string path)
    {
        var formatter = new BinaryFormatter();

        Color[] colors;

        using (var stream = File.OpenRead(path))
        {
            int count = (int)formatter.Deserialize(stream);
            colors = new Color[count];

            for (int index = 0; index < count; index++)
            {
                byte r = (byte)formatter.Deserialize(stream);
                byte g = (byte)formatter.Deserialize(stream);
                byte b = (byte)formatter.Deserialize(stream);

                colors[index] = Color.FromRgb(r, g, b);
            }
        }

        return colors;
    }

    // ******************************************************************

答案 4 :(得分:0)

    public struct MyX
    {
        public int IntValue;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U1)]
        public byte[] Array;

        MyX(int i, int b)
        {
            IntValue = b;
            Array = new byte[3];
        }

        public MyX ToStruct(byte []ar)
        {

            byte[] data = ar;//= { 1, 0, 0, 0, 9, 8, 7 }; // IntValue = 1, Array = {9,8,7}
            IntPtr ptPoit = Marshal.AllocHGlobal(data.Length);
            Marshal.Copy(data, 0, ptPoit, data.Length);

            MyX x = (MyX)Marshal.PtrToStructure(ptPoit, typeof(MyX));
            Marshal.FreeHGlobal(ptPoit);

            return x;
        }
        public byte[] ToBytes()
        {
            Byte[] bytes = new Byte[Marshal.SizeOf(typeof(MyX))];
            GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
            try
            {
                Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
                return bytes;
            }
            finally
            {
                pinStructure.Free();
            }
        }
    }

    void function()
    {
        byte[] data = { 1, 0, 0, 0, 9, 8, 7 }; // IntValue = 1, Array = {9,8,7}
        IntPtr ptPoit = Marshal.AllocHGlobal(data.Length);
        Marshal.Copy(data, 0, ptPoit, data.Length);

        var x = (MyX)Marshal.PtrToStructure(ptPoit, typeof(MyX));
        Marshal.FreeHGlobal(ptPoit);

        var MYstruc = x.ToStruct(data);


        Console.WriteLine("x.IntValue = {0}", x.IntValue);
        Console.WriteLine("x.Array = ({0}, {1}, {2})", x.Array[0], x.Array[1], x.Array[2]);
    }