You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中封送(Marshal)动态大小的结构体数组?

C#结构体包含动态数组的内存序列化问题

为提升网络数据传输效率,想要将多个testStruct结构体打包到包含动态大小数组的sendTestStruct结构体中发送,而非分开发送单个结构体数据包,编写了如下代码:

public struct sendTestStruct
{
    public int numberOfStructs;
    public testStruct[] structs;
}
public struct testStruct 
{
    public float float1, float2;
    public int int1, int2;
    public byte byte1, byte2;
}

sendTestStruct send = new();
send.structs = new testStruct[5];
send.numberOfStructs = 5;
for (var i = 0; i < 5; i++)
{
    testStruct numbers = new();
    numbers.float1 = Random.Range(0f, 10f);
    numbers.float2 = Random.Range(0f, 10f);
    numbers.int1 = Random.Range(0, 10);
    numbers.int2 = Random.Range(0, 10);
    numbers.byte1 = (byte)Random.Range(0, 10);
    numbers.byte2 = (byte)Random.Range(0, 10);
    send.structs[i] = numbers;
}
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(send));
Marshal.StructureToPtr(send, ptr, false);

sendTestStruct newstruct = Marshal.PtrToStructure<sendTestStruct>(ptr);

Marshal.FreeHGlobal(ptr);

执行sendTestStruct newstruct = Marshal.PtrToStructure<sendTestStruct>(ptr);时出现错误:

Structure field of type testStruct[] can't be marshalled as LPArray


解决方案

方法一:添加Marshal特性并手动计算内存大小

问题根源是默认情况下,Marshal无法自动处理结构体中的动态数组,需要通过特性指定数组长度的来源,并手动计算总内存大小:

  1. 修改结构体定义,添加布局特性和数组序列化特性:
[StructLayout(LayoutKind.Sequential)]
public struct sendTestStruct
{
    public int numberOfStructs;
    // SizeParamIndex = 0 表示数组长度由第0个字段(numberOfStructs)指定
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
    public testStruct[] structs;
}

[StructLayout(LayoutKind.Sequential)]
public struct testStruct 
{
    public float float1, float2;
    public int int1, int2;
    public byte byte1, byte2;
}
  1. 手动计算需要分配的内存总大小,替换原Marshal.SizeOf(send)的调用:
sendTestStruct send = new();
send.structs = new testStruct[5];
send.numberOfStructs = 5;
for (var i = 0; i < 5; i++)
{
    testStruct numbers = new();
    numbers.float1 = Random.Range(0f, 10f);
    numbers.float2 = Random.Range(0f, 10f);
    numbers.int1 = Random.Range(0, 10);
    numbers.int2 = Random.Range(0, 10);
    numbers.byte1 = (byte)Random.Range(0, 10);
    numbers.byte2 = (byte)Random.Range(0, 10);
    send.structs[i] = numbers;
}

// 手动计算总内存:结构体数量的大小 + 每个testStruct的大小 × 数量
int singleTestStructSize = Marshal.SizeOf<testStruct>();
int totalMemorySize = Marshal.SizeOf<int>() + send.numberOfStructs * singleTestStructSize;

IntPtr ptr = Marshal.AllocHGlobal(totalMemorySize);
Marshal.StructureToPtr(send, ptr, false);

sendTestStruct newstruct = Marshal.PtrToStructure<sendTestStruct>(ptr);

Marshal.FreeHGlobal(ptr);

方法二:手动序列化/反序列化为字节数组(更适合网络传输)

如果是用于网络传输,直接手动将数据转换为字节数组会更灵活,无需依赖Marshal对结构体数组的处理:

序列化代码(转为可发送的字节数组):

sendTestStruct send = new();
send.structs = new testStruct[5];
send.numberOfStructs = 5;
// 填充数据...

List<byte> byteBuffer = new List<byte>();
// 先写入结构体数量
byteBuffer.AddRange(BitConverter.GetBytes(send.numberOfStructs));

// 逐个序列化testStruct并加入缓冲区
int singleStructSize = Marshal.SizeOf<testStruct>();
foreach (var ts in send.structs)
{
    IntPtr tempPtr = Marshal.AllocHGlobal(singleStructSize);
    Marshal.StructureToPtr(ts, tempPtr, false);
    
    byte[] structBytes = new byte[singleStructSize];
    Marshal.Copy(tempPtr, structBytes, 0, singleStructSize);
    byteBuffer.AddRange(structBytes);
    
    Marshal.FreeHGlobal(tempPtr);
}

byte[] sendData = byteBuffer.ToArray();
// 此时sendData可直接通过网络发送

反序列化代码(从接收的字节数组恢复结构体):

byte[] receivedData = ...; // 从网络接收的字节数组
sendTestStruct receivedStruct = new();

int offset = 0;
// 先读取结构体数量
receivedStruct.numberOfStructs = BitConverter.ToInt32(receivedData, offset);
offset += sizeof(int);

// 逐个恢复testStruct
int singleStructSize = Marshal.SizeOf<testStruct>();
receivedStruct.structs = new testStruct[receivedStruct.numberOfStructs];
for (int i = 0; i < receivedStruct.numberOfStructs; i++)
{
    IntPtr tempPtr = Marshal.AllocHGlobal(singleStructSize);
    Marshal.Copy(receivedData, offset, tempPtr, singleStructSize);
    
    receivedStruct.structs[i] = Marshal.PtrToStructure<testStruct>(tempPtr);
    
    Marshal.FreeHGlobal(tempPtr);
    offset += singleStructSize;
}

内容的提问来源于stack exchange,提问作者theepic0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:50:17