Udostępnij za pośrednictwem


StructLayout attribute magic

I've been reading for a while in the great CLR via C# second edition book, and here is one of the best things I've seen there,
When you declare a type, the CLR in runtime automatically rearrange the order of your type members for performance, so the access to these members are faster, and also trying to use minimum memory, stuff like making the variables reserves even memory locations for faster access and all these stuff, but you still have control on the CLR to force him follow the sequence you defined, and this helps especially in the interoperability with some C/C++ API's that accepts structs, so you have to stick with the structure layout, and in this case you have to add the StructLayout attribute to your class or struct like this

 [StructLayout(LayoutKind.Sequential)]
struct Point
{
   int x;
   int y;
}

If you are declaring a struct type, the default Layout is Sequential, so you don't need to add the StructLayout.
However, developers add the attribute to struct when they want to mention that this struct is defined for interop with unmanaged stuff.

So where is the fun ?? I already know this
The fun comes here, the LayoutKind actually has three options which are

  1. Auto (which is the default behavior)
  2. Sequential
  3. Explicit

The Explicit is how you can have full control of how the Struct members are stuffed in the main memory,
so you can declare the point like this

 [StructLayout(LayoutKind.Explicit)]
struct Point
{
    [FieldOffset(0)]
    public int x;
    [FieldOffset(sizeof(int))]
    public int y;
}

So here I defined the struct member X to reserve the bytes starting from offset 0, then the member Y to reserve the bytes starting from offset 4 which is sizeof(int), so the Y will come immediately after X in memory, However you can play with the numbers like making them overlap together and get the garbage data we used to see in C :).
and as long as we are considering overlapping so we can define something like the old C Union data structure, which we can define a data structure that will contain multiple definitions of variables types and the memory that will be reserved is the memory required to store the largest type. for more information about C/C++ Union
So all what you need here, is setting all the variables' offsets declared in the struct to 0, so all of them occupy the same locations in memory, and the memory reserved for them all is the memory for the largest one, and you are only allowed to use ONLY ONE of them, if you tried assigning values to more than one member, this will lead to unexpected behavior and results, so consider this

 [StructLayout(LayoutKind.Explicit)]
struct UnionLike
{
    [FieldOffset(0)]
    public int iValue;
    [FieldOffset(0)]
    public int lValue;
    [FieldOffset(0)]
    public double dValue; 
}

and this will lead to this memory organization (conceptually)

So the whole UnionLike type reserves 8 bytes in memory which is the size of the largest member (in this case dValue)

This was amazing to me when I first saw it, and I think it worths a try from you.
kick it on DotNetKicks.com