This will work as expected. If you add StructLayout
which specifies a packing to a generic class, it will add a .pack
directive to the IL generated.
For example, given this class:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Test<T1, T2> where T1: struct where T2: struct
{
public T1 One;
public T2 Two;
}
The IL output for this is something like this:
.class public sequential ansi beforefieldinit Test`2<valuetype .ctor ([System.Runtime]System.ValueType) T1, valuetype .ctor ([System.Runtime]System.ValueType) T2>
extends [System.Runtime]System.Object
{
.pack 1
.size 0
.field public !T1 One
.field public !T2 Two
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: ret
}
}
Note the .pack 1
, which specifies the packing.
You can verify that this packing does in fact work by writing some unsafe
code along these lines:
public static class Program
{
public static void Main()
{
var test = new Test<byte, long>
{
One = 1,
Two = 2
};
unsafe
{
fixed (byte* p1 = &test.One)
fixed (long* p2 = &test.Two)
{
Console.WriteLine((byte*)p2 - p1);
}
}
}
}
The output of this when using Pack = 1
is 1
.
If you change the packing to Pack = 4
, the output is 4
.
And if you change the packing to Pack = 8
, the output is 8
.
This demonstrates that the packing in memory is performed, even without using marshalling.
There are many packets of a similar format. So I try to create system that can create new packet types combining many other types. for easy to expansion.
I'm not totally sure what you mean by this, but it could be that "discriminated unions" might be of use to you in the future - but it doesn't look like they will be implemented any time soon, so possibly it will be years before they are available (if ever).
Also note that this only works with blittable types!
See here for the documentation