4

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

考虑到以下情况,我想将我的 Color[](结构数组)转换为字节数组以安全的方式:

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

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

如果不可能,为什么?

谢谢,埃里克

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

4

5 回答 5

7

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

我们将我们Color[]视为一个ReadOnlySpan<Color>. 我们将其重新解释为ReadOnlySpan<byte>. 最后,由于WriteAllBytes没有基于 span 的重载,我们使用 aFileStream将 span 写入磁盘。

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

作为一个有趣的旁注,您还可以尝试将[StructLayout(LayoutKind.Explicit)]用作字段的属性。它允许您指定重叠字段,有效地允许联合的概念。

是 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对象)的部分。:)

于 2019-11-19T15:13:12.697 回答
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();
}
于 2013-07-24T18:37:25.617 回答
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;
}

结果如下:

例子

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

于 2013-07-24T17:14:36.503 回答
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;
    }

    // ******************************************************************
于 2013-07-24T19:54:14.987 回答
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]);
    }
于 2018-02-07T15:08:48.810 回答