I'm investigating on IPC data exchange with non-.Net applications based on Memory Mapped Files. Therefore I'm trying to understand on how C# can organize physical data layouts for that. So I started with unsafe structs as from what I read before this seemed to be the right technology. When playing with different data layouts for bool type values I got confused about this code and the results:
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace UnsafeBoolArrayStruct
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public unsafe struct StructOfBool
{
public bool Bool_00;
public fixed bool Bool_01[1];
public bool Bool_02;
public fixed bool Bool_03[2];
public fixed bool Bool_05[3];
public fixed bool Bool_08[4];
public fixed bool Bool_12[5];
public fixed bool Bool_17[7];
public fixed bool Bool_24[8];
public fixed bool Bool_32[9];
public fixed bool Bool_41[2];
public bool Bool_43;
}
class Program
{
static void Main(string[] args)
{
StructOfBool dummy = new StructOfBool();
int _Size = Marshal.SizeOf(typeof(StructOfBool));
int _Pack = typeof(StructOfBool).StructLayoutAttribute.Pack;
Console.WriteLine("{0} -> Pack: {1}, Size: {2}", typeof(StructOfBool).Name, _Pack, _Size);
foreach (FieldInfo fi in typeof(StructOfBool).GetFields())
{
Type _Type;
int _ArrayLength = 0;
_Size = Marshal.SizeOf(fi.FieldType);
var _Offset = Marshal.OffsetOf(typeof(StructOfBool), fi.Name);
// Check for arrays
var _Attribute = fi.GetCustomAttributes(typeof(FixedBufferAttribute), false);
if (_Attribute.Length > 0)
{ // Array
_Type = ((FixedBufferAttribute)_Attribute[0]).ElementType;
_ArrayLength = ((FixedBufferAttribute)_Attribute[0]).Length;
}
else
// Singular field
_Type = fi.FieldType;
// Process found data
Console.WriteLine("{0} -> Type: {1}, ArrayLength: {2}, Offset: {3}, Size: {4}", fi.Name, Type.GetTypeCode(_Type), _ArrayLength, _Offset, _Size);
}
Console.ReadKey();
}
}
}
The result shows like this:
StructOfBool -> Pack: 2, Size: 64
Bool_00 -> Type: Boolean, ArrayLength: 0, Offset: 0, Size: 4
Bool_01 -> Type: Boolean, ArrayLength: 1, Offset: 4, Size: 4
Bool_02 -> Type: Boolean, ArrayLength: 0, Offset: 8, Size: 4
Bool_03 -> Type: Boolean, ArrayLength: 2, Offset: 12, Size: 4
Bool_05 -> Type: Boolean, ArrayLength: 3, Offset: 16, Size: 4
Bool_08 -> Type: Boolean, ArrayLength: 4, Offset: 20, Size: 4
Bool_12 -> Type: Boolean, ArrayLength: 5, Offset: 24, Size: 5
Bool_17 -> Type: Boolean, ArrayLength: 7, Offset: 30, Size: 7
Bool_24 -> Type: Boolean, ArrayLength: 8, Offset: 38, Size: 8
Bool_32 -> Type: Boolean, ArrayLength: 9, Offset: 46, Size: 9
Bool_41 -> Type: Boolean, ArrayLength: 2, Offset: 56, Size: 4
Bool_43 -> Type: Boolean, ArrayLength: 0, Offset: 60, Size: 4
It looks like that packing is handled differently at least on singular fields compared to arrays. Why does Bool00 with one bool value take 4 bytes, Bool03 with two bool values also take 4 bytes, but Bool12 with five values just 5 bytes??
Does anybody know, why?