在C#中将对象列表转换为字节数组

3

我有这个结构体定义:

public struct ChangedByte
{
    public byte R;
    public byte G;
    public byte B;
    public int x;
    public int y;
}

我创建了一个:

List<ChangedByte> testme = new List<ChangedByte>();

(并添加项目到其中)
我将其转换为数组:
ChangedByte[] alpha = testme.ToArray();

我找到了一个与SO上类似问题相关的函数:

byte[] StructureToByteArray(object obj)
{
    int len = Marshal.SizeOf(obj);
    byte[] arr = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.StructureToPtr(obj, ptr, true);
    Marshal.Copy(ptr, arr, 0, len);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

我会这样称呼它:
byte[] test = StructureToByteArray(alpha);

我得到了以下错误信息:
“Type 'new_encoder.Form1+ChangedByte[]' 无法作为非托管结构进行封送;无法计算有意义的大小或偏移量。”
我想把一个对象列表转换成字节数组(可能避免序列化,因为它会增加数组的大小)。这可行吗?
另外:
我已经尝试将结构体修改为以下内容:
public struct ChangedByte
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public byte R;
    [MarshalAs(UnmanagedType.LPWStr)]
    public byte G;
    [MarshalAs(UnmanagedType.LPWStr)]
    public byte B;
    [MarshalAs(UnmanagedType.LPWStr)]
    public int x;
    [MarshalAs(UnmanagedType.LPWStr)]
    public int y;
}

但是仍然出现相同的错误。

1
所以,我自己没有测试过,但你可以看一下MarshalAs-属性。这是一个非常类似的问题:https://social.msdn.microsoft.com/Forums/vstudio/en-US/6666b71c-5dc6-4bcc-8aae-64f406ff3690/sizeof-and-marshalsizeof-not-working-on-struct?forum=csharpgeneral希望能有所帮助。 - Dominik
@Dominik 你好,谢谢你的发布。我尝试了一下但是还是出现了同样的错误。可能是我没有理解信息...感谢提供链接 :) - Andrew Simpson
LPWStr 用于字符串,将其应用于 int 或 byte 字段是没有意义的。 - Thomas Levesque
2个回答

2
您可以使用以下方法:
static byte[] ArrayToBytes<T>(T[] array) where T : struct
{
    IntPtr ptr = IntPtr.Zero;
    try
    {
        int len = Marshal.SizeOf(typeof(T));
        int totalLen = array.Length * len;
        ptr = Marshal.AllocHGlobal(len);
        for (int i = 0; i < array.Length; i++)
        {
            Marshal.StructureToPtr(array[i], ptr + i * len, false);
        }
        var bytes = new byte[totalLen];
        Marshal.Copy(ptr, bytes, 0, totalLen);
        return bytes;
    }
    finally
    {
        if (ptr != IntPtr.Zero)
            Marshal.FreeHGlobal(ptr);
    }
}

static T[] ArrayFromBytes<T>(byte[] bytes) where T : struct
{
    IntPtr ptr = IntPtr.Zero;
    try
    {
        int len = Marshal.SizeOf(typeof(T));
        int count = bytes.Length / len;
        ptr = Marshal.AllocHGlobal(bytes.Length);
        Marshal.Copy(bytes, 0, ptr, bytes.Length);
        T[] array = new T[count];
        for (int i = 0; i < count; i++)
        {
            array[i] = Marshal.PtrToStructure<T>(ptr + i * len);
        }
        return array;
    }
    finally
    {
        if (ptr != IntPtr.Zero)
            Marshal.FreeHGlobal(ptr);
    }
}

然后像这样使用它们:

byte[] test = ArrayToBytes(alpha); // serialize
ChangedByte[] alpha2 = ArrayFromBytes<ChangedByte>(test); // deserialize

嗨,感谢您的回答。我已经实现了它,并在这行代码上得到了相同的错误:int len = Marshal.SizeOf(typeof(T)); - Andrew Simpson
嗨,当我从结构体字段中删除这些属性时,我会得到一个不同的错误,我会遇到访问冲突问题。 - Andrew Simpson
@AndrewSimpson,我已经尝试使用你提供的ChangedByte结构,它对我来说很好用。请尝试在该结构上添加[StructLayout(LayoutKind.Sequential)]属性。 - Thomas Levesque
嗨 - 謝謝。我知道我漏掉了什麼。謝謝 :) - Andrew Simpson
@Thomas,干得好。我已经修改了ArrayFromBytes方法,将'array[i] = Marshal.PtrToStructure<T>(ptr + i * len);'改为'array[i] = (T)Marshal.PtrToStructure(ptr + i * len, typeof(T));'。 - Changju.rhee

2
在这种情况下,由于你不仅要转换结构体,还要转换结构体数组,因此你需要循环遍历数组并转换每个元素。使用指针算术运算来移动缓冲区并添加项目。
public static byte[] StructureArrayToByteArray(ChangedByte[] objs)
{
    int structSize = Marshal.SizeOf(typeof(ChangedByte));
    int len = objs.Length * structSize;
    byte[] arr = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    for (int i = 0; i < objs.Length; i++ ) {
        Marshal.StructureToPtr(objs[i], ptr + i*structSize, true);
    }
    Marshal.Copy(ptr, arr, 0, len);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

嗨,你的回答很有道理,我已经实现了它,但是在这行代码上我仍然收到了相同的错误: int structSize = Marshal.SizeOf(typeof(ChangedByte)); - Andrew Simpson
错误是否略有不同,例如这次它指出的是哪种类型导致了问题? - MatthewG
哦,我现在看到了那个错误,当我将[MarshalAs(UnmanagedType.LPWStr)]添加到结构体中时。如果没有这些属性,我就不会得到编译错误。如果没有这些属性,它对你是否有效,或者在那种情况下它是否无法正确地进行封送? - MatthewG
你好,感谢您一直以来的关注。这是消息:类型 'new_encoder.Form1+ChangedByte' 无法作为非托管结构进行编组;无法计算有意义的大小或偏移量。 - Andrew Simpson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接