简体   繁体   中英

Does work the StructLayout attribute with generic in C#?

There is a code using the StructLayout attribute with generic as below.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Packet<H, Body1, Body2> : IOutlinePacket
    where H : new()
    where Body1 : IDeviceBody, new()
    where Body2 : INetworkBody, new()
{
    H Header = new Header();
    Body1 DeviceBody = new Body1();
    Body2 NetworkBody = new Body2();
}

If the class is not generic the sequence of H, Body1, Body2 will be guaranteed and will be allocated by a 1byte step. But I don't know how about the generic type case.

Of course, I know the generic type is not supported the marshal function but I want to know how about StructLayout case.

I tried to a few test to check this and according to a tests, it seems to be work but I don't know whether it is a coincidence or it is always work.

I'm sorry for my terrible English and Thank you for reading.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM