C# 将可变长度结构转换为字节[]

C# 将可变长度结构转换为字节[],c#,serialization,data-structures,C#,Serialization,Data Structures,我正在尝试向硬件设备发送网络数据包。我想使用一种灵活的、面向对象的方法,这样我就可以在更高的层次上使用它。数据包有几个长度可变的字段。显然,字节布局非常特殊 这是一个表示我需要发送的数据包的结构: [StructLayout(LayoutKind.Sequential)] public struct Packet { public UInt16 Instruction; public UInt16 Length; //length of data field public UI

我正在尝试向硬件设备发送网络数据包。我想使用一种灵活的、面向对象的方法,这样我就可以在更高的层次上使用它。数据包有几个长度可变的字段。显然,字节布局非常特殊

这是一个表示我需要发送的数据包的结构:

[StructLayout(LayoutKind.Sequential)]
public struct Packet
{
   public UInt16 Instruction;
   public UInt16 Length; //length of data field
   public UInt32 SessionHandle;

   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   public byte[] SenderContext;

   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   public byte[] MessageData;
}
MessageData[]
可以是任意长度,我将它固定为8字节作为起始

下面是我试图创建一个
字节[]
以通过套接字发送它的尝试:

public static byte[] ToBytes(Packet ep)
{
   Byte[] bytes = new Byte[Marshal.SizeOf(typeof(Packet))];
   GCHandle pinStructure = GCHandle.Alloc(ep, GCHandleType.Pinned);
   try
   {
      Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
      return bytes;
   }
   finally
   {
      pinStructure.Free();
   }
}
但我收到:

ArgumentException : Object contains non-primitive or non-blittable data.
我以为在结构中设置
SizeConst
可以解决这个问题?无论如何,我比这更迷茫,因为硬件设备期望一个可变长度的数据包,我想利用它

我可以手动将数据包一个字节一个字节地放在一起,一切都很好。但我知道一定有更好的办法,我肯定走错了路


有什么想法吗?

CLR不允许您使用字段与引用类型重叠的结构。您的案例中的两个数组。它与垃圾收集器非常不兼容,它无法跟踪对象引用。并且会非常不安全,因为它允许后门进入堆,直接操纵对象引用的值


在您的特定情况下,这样做没有意义,因为两个数组重叠并且大小完全相同。我们可以很好地完成这项工作。

问题是数组既不能进行blittable,也不能打包到结构中。它们毕竟是引用类型。因此,结构实际上只包含对堆上的数组对象的引用。据我所知,
marshallas
属性不会影响成员的布局。它只影响在互操作例程期间封送成员的方式

我的另一个观察结果是,实际上是固定结构的装箱版本,而不是实际的结构。当然,值类型存在于堆栈上,因此无需固定它。也许您已经知道了,这就是您选择从中提取
IntPtr
的方法。这很好,但是通过使用
unsafe
code或
Marshal.StructureToPtr
方法,您可以更高效、更恰当地完成相同的任务


我认为您最好创建一个单独的
字节
数组,并使用
数组。复制
方法和
位转换器
类。

这很有意义,谢谢。如果您是我,您将如何构造I/O,以便能够发送外部设备数据包,这些数据包具有描述数据包的固定值(如枚举)和可变大小的单个字节[]。BinaryWriter是一种枯燥但始终有效的解决方案。Marshal.StructureToPtr后跟Marshal.Copy有点粗糙。固定结构不好,其布局只有在偶然情况下才能兼容。请注意,应该在[StructLayout]中使用Pack=1。