C#-将字节数组强制转换为结构数组,反之亦然(反向)

C#-将字节数组强制转换为结构数组,反之亦然(反向),c#,arrays,casting,struct,byte,C#,Arrays,Casting,Struct,Byte,我想将颜色[]保存到文件中。为此,我发现使用“System.IO.file.writealBytes”将字节数组保存到文件中应该非常有效 考虑到以下因素,我想以安全的方式将颜色[](结构数组)转换为字节数组: little endian/big endian的潜在问题(我认为不可能发生,但希望确定) 有两个指向同一内存的不同指针,指向不同的类型。垃圾收集是否知道要做什么-移动对象-删除指针 如果可能的话,最好有一种通用的方法将字节数组转换为任意结构的数组(T struct),反之亦然 如果不

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

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

  • little endian/big endian的潜在问题(我认为不可能发生,但希望确定)
  • 有两个指向同一内存的不同指针,指向不同的类型。垃圾收集是否知道要做什么-移动对象-删除指针
如果可能的话,最好有一种通用的方法将字节数组转换为任意结构的数组(T struct),反之亦然

如果不可能,为什么

谢谢, 埃里克

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


如果您真的想复制每个字节,而不是复制同一个对象,则可以使用指针,如下所示:

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);
结果如下:


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

关于数组类型转换

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

只有在可以隐式或显式转换变量类型的情况下,才能在内存中使两个不同类型的变量指向同一对象。从一个元素类型的数组转换到另一个元素类型并不是一项简单的任务:它必须转换跟踪数组长度等内容的内部成员

向文件写入和读取颜色[]的简单方法

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();
}
void WriteColorsToFile(字符串路径,颜色[]颜色)
{
BinaryWriter=新的BinaryWriter(File.OpenWrite(path));
书写者。书写(颜色。长度);
foreach(颜色中的颜色)
{
writer.Write(color.ToArgb());
}
writer.Close();
}
Color[]ReadColorsFromFile(字符串路径)
{
BinaryReader=新的BinaryReader(File.OpenRead(path));
int length=reader.ReadInt32();
颜色[]结果=新颜色[长度];

对于(int n=0;n工作代码,以供参考(注意,我的示例中不需要alpha通道):

//************************************************************************
//如果有一天微软让颜色序列化。。。
//公共静态void SaveColors(颜色[]颜色,字符串路径)
//{
//BinaryFormatter bf=新的BinaryFormatter();
//MemoryStream ms=新的MemoryStream();
//bf.序列化(ms、颜色);
//byte[]bytes=ms.ToArray();
//File.writealBytes(路径,字节);
//}
//如果有一天微软让颜色序列化。。。
//公共静态颜色[]加载颜色(字符串路径)
//{
//Byte[]bytes=File.ReadAllBytes(路径);
//BinaryFormatter bf=新的BinaryFormatter();
//MemoryStream ms2=新的MemoryStream(字节);
//返回(颜色[])bf.反序列化(ms2);
//}
// ******************************************************************
公共静态void SaveColorsToFile(颜色[]颜色,字符串路径)
{
var formatter=新的二进制格式化程序();
int count=颜色。长度;
使用(var stream=File.OpenWrite(path))
{
序列化(流,计数);
for(int index=0;index
自.NET Core 2.1以来,是的,我们可以!输入
MemoryMarshal

我们将把
Color[]
视为
ReadOnlySpan
。我们将其重新解释为
ReadOnlySpan
。最后,由于
writealBytes
没有基于span的重载,我们使用
FileStream
将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
字段重叠


这似乎与您正在尝试的操作密切相关。它让您非常接近,除了高效地写入所有字节(尤其是多个
颜色
对象)的部分之外。

我认为您理解正确,但是如果GC决定将对象移到其他地方会发生什么。我需要一个s
    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]);
    }
var byteSpan = MemoryMarshal.AsBytes(colorArray.AsSpan());
fileStream.Write(byteSpan);
[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
    [FieldOffset(0)]
    public UInt16 myInt;

    [FieldOffset(0)]
    public Byte byte1;

    [FieldOffset(1)]
    public Byte byte2;
}