C# StructLayout Pack=?? для использования со значениями bool
В C# я создаю несколько различных структур, которые содержат 16 переменных типа bool. У меня будет несколько различных из этих структур, которые затем будут объединены с другими типами данных в более сложные структуры. Мне нужно, чтобы они обрабатывались как 2 байта в длину. В приведенном ниже коде переменная, созданная с типом CtrlWord1, при выполнении Marshal.SizeOf будет иметь длину 64 независимо от того, создается ли она со значением Pack 0, 1 или 2.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
public bool a1;
public bool a2;
public bool a3;
public bool a4;
public bool a5;
public bool a6;
public bool a7;
public bool a8;
public bool b1;
public bool b2;
public bool b3;
public bool b4;
public bool c1;
public bool c2;
public bool c3;
public bool c4;
}
2 ответа
Хотя bool
тип в C# имеет размер всего 1 байт (sizeof(bool) == 1
), CLR по умолчанию использует его как неуправляемый BOOL
тип. Это размер, который вы получаете, когда вы звоните Marshal.SizeOf
,
BOOL
является typedef в заголовках Windows SDK для int
, что составляет 4 байта в размере. Зачем? Потому что эти заголовки были написаны для языка Си в то время, когда у этого языка не было первоклассного логического типа. Это происходит сейчас, но решения зафиксированы в камне по причинам обратной совместимости. Маршалы ЦПР bool
печатает таким образом, чтобы быть совместимым с функциями Windows API, которые используют BOOL
значения, поскольку взаимодействие с Windows API является наиболее распространенным использованием P/Invoke. (По той же причине, что соглашение о вызовах по умолчанию для подписей P / Invoke - это stdcall вместо cdecl.)
Чтобы сказать CLR относиться к вашему bool
s как 1-байтовые bools, а не 4-байтовые BOOL
s, используйте MarshalAs
атрибут К сожалению, вы должны использовать его 16 раз:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
[MarshalAs(UnmanagedType.I1)] // marshal as a 1-byte signed int, not a 4-byte BOOL
public bool a1;
// etc.
}
Это гарантирует, что ваша структура будет только 16 байтов.
Тем не менее, нет никакого магического атрибута для создания битового поля. Вы должны будете создать и управлять этим самостоятельно, используя Int32
тип. Или используйте BitArray
тип.
Глорин Оукенфут сказал это гораздо лучше, чем мог, поэтому я просто процитирую его
Упаковка / верстка выполняется на уровне байтов. Это означает, что bool никогда не займет меньше байта, полагаясь исключительно на упаковку. Вам придется сделать что-то более сложное, например, использовать два закрытых байтовых поля и несколько свойств, которые ссылаются на соответствующие биты в этих байтах.
Вот реализация этого, каждый элемент, который вы увеличиваете с правой стороны 1 << _
перейти к следующему битовому полю.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
private Int16 _backingField;
private void SetBitfield(Int16 mask, bool value)
{
if (value)
{
_backingField = (Int16)(_backingField | mask);
}
else
{
_backingField = (Int16)(_backingField & ~mask);
}
}
private bool GetBitfield(Int16 mask)
{
return (_backingField & A1_MASK) != 0;
}
private const Int16 A1_MASK = 1 << 0;
public bool a1
{
get { return GetBitfield(A1_MASK); }
set { SetBitfield(A1_MASK, value); }
}
private const Int16 A2_MASK = 1 << 1;
public bool a2
{
get { return GetBitfield(A2_MASK); }
set { SetBitfield(A2_MASK, value); }
}
private const Int16 A3_MASK = 1 << 2;
public bool a3
{
get { return GetBitfield(A3_MASK); }
set { SetBitfield(A3_MASK, value); }
}
private const Int16 A4_MASK = 1 << 3;
public bool a4
{
get { return GetBitfield(A4_MASK); }
set { SetBitfield(A4_MASK, value); }
}
//And so on
}