C#中字节数组与结构体互转的标准实现方法咨询
字节数组与C#结构体的互转(大端字节序)
首先明确:.NET 没有直接提供泛型方法可以自动完成大端字节序下的字节数组与结构体双向转换——默认的内存操作类(如Marshal)依赖系统原生字节序(通常是小端),且需要手动处理结构体布局和字节序转换。不过可以通过以下两种可靠方案实现需求:
方案1:手动解析/序列化(推荐,轻量高效)
利用.NET Core 2.1+ 提供的BinaryPrimitives类,它专门支持大端/小端的基元类型读写,无需额外分配内存,适合简单结构体场景。
步骤1:定义带布局属性的结构体
确保结构体无内存对齐的 padding,避免字节长度不匹配:
[StructLayout(LayoutKind.Sequential, Pack = 1)] struct Envelop { public ushort Value1; public uint Value2; }
步骤2:字节数组转结构体
直接从字节数组的Span中读取大端数据:
static Envelop BytesToEnvelop(byte[] bytes) { if (bytes.Length != Marshal.SizeOf<Envelop>()) throw new ArgumentException("字节数组长度不匹配"); return new Envelop { Value1 = BinaryPrimitives.ReadUInt16BigEndian(bytes.AsSpan(0, 2)), Value2 = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(2, 4)) }; }
步骤3:结构体转字节数组
将结构体字段按大端顺序写入字节数组:
static byte[] EnvelopToBytes(Envelop envelop) { byte[] bytes = new byte[Marshal.SizeOf<Envelop>()]; BinaryPrimitives.WriteUInt16BigEndian(bytes.AsSpan(0, 2), envelop.Value1); BinaryPrimitives.WriteUInt32BigEndian(bytes.AsSpan(2, 4), envelop.Value2); return bytes; }
测试代码
byte[] inputBytes = { 0x10, 0x00, 0x01, 0x00, 0x01, 0x00 }; Envelop data = BytesToEnvelop(inputBytes); Console.WriteLine(data.Value1); // 输出16 Console.WriteLine(data.Value2); // 输出257 byte[] outputBytes = EnvelopToBytes(data); // 验证输出与输入一致:outputBytes应等于inputBytes
方案2:使用Marshal类(适合复杂结构体)
如果结构体字段较多,可借助Marshal类实现内存拷贝,但需手动处理字节序转换(Marshal默认使用系统字节序)。
步骤1:结构体定义(同方案1)
[StructLayout(LayoutKind.Sequential, Pack = 1)] struct Envelop { public ushort Value1; public uint Value2; }
步骤2:字节数组转结构体
先将大端字节数组转换为系统字节序(小端),再通过内存拷贝到结构体:
static Envelop BytesToEnvelopMarshal(byte[] bytes) { if (bytes.Length != Marshal.SizeOf<Envelop>()) throw new ArgumentException("字节数组长度不匹配"); // 转换大端字节为系统字节序 byte[] systemOrderBytes = new byte[bytes.Length]; // 处理Value1(ushort) Array.Reverse(bytes, 0, 2); Array.Copy(bytes, 0, systemOrderBytes, 0, 2); // 处理Value2(uint) Array.Reverse(bytes, 2, 4); Array.Copy(bytes, 2, systemOrderBytes, 2, 4); // 内存拷贝到结构体 GCHandle handle = GCHandle.Alloc(systemOrderBytes, GCHandleType.Pinned); try { return Marshal.PtrToStructure<Envelop>(handle.AddrOfPinnedObject()); } finally { handle.Free(); } }
步骤3:结构体转字节数组
先将结构体拷贝到字节数组,再转换为大端字节序:
static byte[] EnvelopToBytesMarshal(Envelop envelop) { byte[] systemOrderBytes = new byte[Marshal.SizeOf<Envelop>()]; GCHandle handle = GCHandle.Alloc(systemOrderBytes, GCHandleType.Pinned); try { Marshal.StructureToPtr(envelop, handle.AddrOfPinnedObject(), false); } finally { handle.Free(); } // 转换系统字节序为大端 byte[] bigEndianBytes = new byte[systemOrderBytes.Length]; // 处理Value1 Array.Reverse(systemOrderBytes, 0, 2); Array.Copy(systemOrderBytes, 0, bigEndianBytes, 0, 2); // 处理Value2 Array.Reverse(systemOrderBytes, 2, 4); Array.Copy(systemOrderBytes, 2, bigEndianBytes, 2, 4); return bigEndianBytes; }
注意事项
- 必须给结构体添加
[StructLayout(LayoutKind.Sequential, Pack = 1)],确保字段按定义顺序排列且无内存对齐 padding,否则字节数组长度会和结构体大小不匹配。 - 如果使用.NET Framework(而非.NET Core/.NET 5+),
BinaryPrimitives不可用,可改用BinaryReader结合手动反转字节的方式实现大端读取。
内容的提问来源于stack exchange,提问作者Infarch
相关产品推荐
相关产品推荐

