When running this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace StructLayoutTest
{
class Program
{
unsafe static void Main()
{
Console.WriteLine(IntPtr.Size);
Console.WriteLine();
Sequential s = new Sequential();
s.A = 2;
s.B = 3;
s.Bool = true;
s.Long = 6;
s.C.Int32a = 4;
s.C.Int32b = 5;
int* ptr = (int*)&s;
Console.WriteLine(ptr[0]);
Console.WriteLine(ptr[1]);
Console.WriteLine(ptr[2]);
Console.WriteLine(ptr[3]);
Console.WriteLine(ptr[4]);
Console.WriteLine(ptr[5]);
Console.WriteLine(ptr[6]);
Console.WriteLine(ptr[7]); //NB!
Console.WriteLine("Press any key");
Console.ReadKey();
}
[StructLayout(LayoutKind.Explicit)]
struct Explicit
{
[FieldOffset(0)]
public int Int32a;
[FieldOffset(4)]
public int Int32b;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct Sequential
{
public int A;
public int B;
public bool Bool;
public long Long;
public Explicit C;
}
}
}
I expect this output BOTH on x86 and x64:
4 or 8 (depending on x86 or x64)
2
3
1
6
0
4
5
garbage
What I get instead on x86:
4
6
0
2
3
1
4
5
garbage
What I get instead on x64:
8
6
0
2
3
1
0
4
5
More:
- The problem goes away when I remove the LayoutKind.Explicit and FieldOffset attributes.
- The problem goes away when I remove the Bool field.
- The problem goes away when I remove the Long field.
- Note that on x64 it seems that the Pack=4 attribute parameter is ignored too?
This applies in .Net3.5 and also .Net4.0
My question: what am I missing? Or is this a bug?
I found a similar question:
Why does LayoutKind.Sequential work differently if a struct contains a DateTime field?
But in my case the layout changes even when the attribute of the substruct changes, without any changes to data types. So it does not look like an optimization. Besides that, I would like to point out that the other question is still unanswered.
In that other question they mention that the layout is respected when using Marshalling. I havent tested that myself but I wonder why is the layout not respected for unsafe code, since all the relevant attributes seem to be in place? Does the documentation mention somewhere that these attributes are ignored unless Marshalling is done? Why?
Considering this, can I even expect LayoutKind.Explicit to work reliably for unsafe code?
Moreover, the documentation mentions the motive of keeping structs with expected layout:
To reduce layout-related problems associated with the Auto value, C#, Visual Basic, and C++ compilers specify Sequential layout for value types.
But this motive apparently does not apply to unsafe code?