【问题标题】:nesting arrays of C# classesC# 类的嵌套数组
【发布时间】:2020-12-30 04:10:27
【问题描述】:

几周前我发布了一个相关问题:Marshal.Sizeof() returning unexpected value

作为背景,我们聘请了第三方将一个旧的 C++ 项目转换为 C#。这是一个通过以太网发送/接收消息的通信协议应用程序,其中所有消息都包含有效负载,这些有效负载是已定义结构的序列化表示:

typedef struct      // size=10
{
    ushort group;
    ushort line;
    ushort v_group; 
    byte ip_address[4];
}GROUP_T;

typedef struct      // size=91
{
    byte struct_version;
    ushort region_id;
    byte address[8];
    GROUP_T groups[8];
} LCT_T;

这些已转换为 C# 类:

[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class GROUP_T
{
        public ushort group;
        public ushort line;
        public ushort v_group;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)]
        public byte[] ip_address = new byte[4];
}

[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class LCT_T
{
        public byte struct_version;
        public ushort region_id;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] address = new byte[8];
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
        public byte[] group_config_bytes = new byte[80];
}

我的问题在于 LCT_T 类的 group_config_bytes 元素。以编程方式这是可行的,但是 GROUP_T 结构的原始数组丢失了等效的字节数组(最初,GROUP_T 数组为空且未使用)。现在我需要为单个 GROUP_T 对象设置值,所以我需要嵌套数组的类版本:

[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class LCT_T
{
        public byte struct_version;
        public ushort region_id;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] address = new byte[8];
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.Struct,SizeConst = 10)]
        public GROUP_T[] groups = new GROUP_T[8];
}

这可以编译,但是 Marshal.SizeOf(typeof(LCT_T)) 返回错误的大小(应该是 11 + (8 * 10) = 91)。

如果没有这个更新的 LCT_T 类定义,如果我需要设置单个组的元素,我必须直接将值插入 group_config_bytes ,这很丑陋,容易出错,而且未来的维护者也不清楚这段代码。

那么:在类中定义嵌套类数组的正确方法是什么?

【问题讨论】:

  • 那么是GROUP_T groups[8] 还是new byte[80]
  • 当前新字节[80]。我需要将其更改为 group_t 类:GROUP_T groups[8]。
  • 哦,我明白了。你为什么不像你应该的那样在LCT_T 中有[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public GROUP_T[] groups?将所有这些类转换为结构并执行此操作。
  • 因为当我这样做时,新类 LCT_T 被序列化,大小是错误的。我认为这就是我在 GROUP_T[] 元素之前使用编组指令的方式。

标签: c# arrays marshalling structlayout


【解决方案1】:

正如@GSerg 所说,您应该使用结构。这些类还有其他相关的东西。而且我认为正如他所说的那样,编组指令导致了错误的大小。这是一个更好的例子,将组提取出来,这样更明显。

[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GROUP_T
{
    public static GROUP_T Default = new GROUP_T() { ip_address = new byte[4] };

    public ushort group;
    public ushort line;
    public ushort v_group;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)]
    public byte[] ip_address;
}

[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LCT_T
{
    public byte struct_version;
    public ushort region_id;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] address;
    
}

public struct LCT_T_WITH_GROUP 
{
    public LCT_T lct;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 8)]
    public GROUP_T[] groups;
}

class Program
{
    static void Main(string[] _)
    {
        int sizeOfGroup = Marshal.SizeOf(typeof(GROUP_T));
        int sizeOfStruct = Marshal.SizeOf(typeof(LCT_T));
        int sizeOfLctWithGroup = Marshal.SizeOf(typeof(LCT_T_WITH_GROUP));

        Console.WriteLine($"GROUP_T: {sizeOfGroup}");
        Console.WriteLine($"LCT_T: {sizeOfStruct}");
        Console.WriteLine($"LCT_T_WITH_GROUP: {sizeOfLctWithGroup}");
    }
}

这个输出是: GROUP_T:10 LCT_T:11 LCT_T_WITH_GROUP:91

【讨论】:

    猜你喜欢
    • 2021-10-03
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多