buzzard51
buzzard51

Reputation: 1467

nesting arrays of C# classes

I posted a related question a few weeks ago : Marshal.Sizeof() returning unexpected value

As background, we hired a third party to convert an old C++ project to C#. This is a communications protocol application that sends/receives messages over ethernet, where all messages contain payloads that are serialized representations of defined structures:

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;

These were converted to C# classes:

[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];
}

My problem is with the group_config_bytes element of the LCT_T class. Programmatically this works, but the original array of GROUP_T structs was lost to the equivalent byte array (originally, the GROUP_T array was empty and unused). Now I need to set values for individual GROUP_T objects, so I need the nested-array-of-classes version:

[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];
}

This compiles, but Marshal.SizeOf(typeof(LCT_T)) is returning the wrong size (should be 11 + (8 * 10) = 91).

Without this updated LCT_T class definition, if I need to set elements of individual groups, I have to poke values directly into group_config_bytes , which is ugly, prone to errors, and unclear to future maintainers of this code.

So: what's the right way to define nested arrays of classes within a class?

Upvotes: 0

Views: 174

Answers (1)

MikeJ
MikeJ

Reputation: 1369

As @GSerg said, you should us structs. The classes have other things associated with them. And I think as he also said the marshaling directive is causing the wrong size. Here's a better example, extracting the Groups out so it's more obivous.

[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}");
    }
}

Output of this is: GROUP_T: 10 LCT_T: 11 LCT_T_WITH_GROUP: 91

Upvotes: 2

Related Questions