Да, вы можете сделать это.Вы находитесь на правильном пути, ответ заключается в использовании BitVector32 вместе с атрибутами FieldOffset и StructLayout .Однако при этом необходимо помнить о нескольких вещах:
Вам необходимо явно указать размер переменных, которые будут содержать данные.Этот первый элемент очень , на который важно обратить внимание.Например, в приведенном выше вопросе вы указываете info как unsigned int .Какой размер без знака int ?32 бита?64 бита?Это зависит от версии ОС, в которой работает данная конкретная версия .NET (это может быть .NET Core, Mono или Win32 / Win64).
Что такое «порядковый номер» илибитовый порядок это?Опять же, мы можем работать на любом типе оборудования (например, Mobile / Xamarin, а не только на ноутбуке или планшете) - поэтому вы не можете принимать битовый порядок Intel.
Нам понадобитсячтобы избежать любого управления памятью, которое зависит от языка, или в языке POD (обычные старые данные) на языке C / C ++.Это будет означать придерживаться только типов значений.
Я собираюсь сделать предположение, основанное на вашем вопросе и спецификации флагов 0-31, что sizeof (int) == 32 .
Хитрость заключается в том, чтобы обеспечить следующее:
- Все данные выровнены по байту.
- Поля битов и поле info выровнены ната же граница байта.
Вот как мы можем это сделать:
[StructLayout(LayoutKind.Explicit, Size = 1, CharSet = CharSet.Ansi)]
public struct MyUnion
{
#region Lifetime
/// <summary>
/// Ctor
/// </summary>
/// <param name="foo"></param>
public MyUnion(int foo)
{
// allocate the bitfield
info = new BitVector32(0);
// initialize bitfield sections
flag1 = BitVector32.CreateSection(1);
flag2 = BitVector32.CreateSection(1, flag1);
flag3 = BitVector32.CreateSection(1, flag2);
}
#endregion
#region Bifield
// Creates and initializes a BitVector32.
[FieldOffset(0)]
private BitVector32 info;
#endregion
#region Bitfield sections
/// <summary>
/// Section - Flag 1
/// </summary>
private static BitVector32.Section flag1;
/// <summary>
/// Section - Flag 2
/// </summary>
private static BitVector32.Section flag2;
/// <summary>
/// Section - Flag 3
/// </summary>
private static BitVector32.Section flag3;
#endregion
#region Properties
/// <summary>
/// Flag 1
/// </summary>
public bool Flag1
{
get { return info[flag1] != 0; }
set { info[flag1] = value ? 1 : 0; }
}
/// <summary>
/// Flag 2
/// </summary>
public bool Flag2
{
get { return info[flag2] != 0; }
set { info[flag2] = value ? 1 : 0; }
}
/// <summary>
/// Flag 1
/// </summary>
public bool Flag3
{
get { return info[flag3] != 0; }
set { info[flag3] = value ? 1 : 0; }
}
#endregion
#region ToString
/// <summary>
/// Allows us to represent this in human readable form
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"Name: {nameof(MyUnion)}{Environment.NewLine}Flag1: {Flag1}: Flag2: {Flag2} Flag3: {Flag3} {Environment.NewLine}BitVector32: {info}{Environment.NewLine}";
}
#endregion
}
Обратите особое внимание на конструктор.По определению, вы не можете определить конструктор по умолчанию для структур в C #.Однако нам нужен какой-то способ обеспечить правильную инициализацию объекта BitVector32 и его разделов перед использованием.Мы достигаем этого, требуя конструктор, который принимает фиктивный целочисленный параметр, и инициализируем объект следующим образом:
/// <summary>
/// Main entry point
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
// brew up one of these...
var myUnion = new MyUnion(0)
{
Flag2 = true
};
Кстати, вы никоим образом не ограничены отдельными битовыми полями - вы можете определитьлюбой размер битового поля, который вам нравится.Например, если бы я изменил ваш пример на:
union _myUnion
{
unsigned int info;
struct
{
unsigned int flag1 : 3 // bit 0-2
unsigned int flag2 : 1 // bit 3
unsigned int flag3 : 4 // bit 4-7
.
.
.
unsigned int flag31 : 1 // bit 31
}
}
Я бы просто изменил свой класс на:
[StructLayout(LayoutKind.Explicit, Size = 1, CharSet = CharSet.Ansi)]
public struct MyUnion2
{
#region Lifetime
/// <summary>
/// Ctor
/// </summary>
/// <param name="foo"></param>
public MyUnion2(int foo)
{
// allocate the bitfield
info = new BitVector32(0);
// initialize bitfield sections
flag1 = BitVector32.CreateSection(0x07);
flag2 = BitVector32.CreateSection(1, flag1);
flag3 = BitVector32.CreateSection(0x0f, flag2);
}
#endregion
#region Bifield
// Creates and initializes a BitVector32.
[FieldOffset(0)]
private BitVector32 info;
#endregion
#region Bitfield sections
/// <summary>
/// Section - Flag1
/// </summary>
private static BitVector32.Section flag1;
/// <summary>
/// Section - Flag2
/// </summary>
private static BitVector32.Section flag2;
/// <summary>
/// Section - Flag3
/// </summary>
private static BitVector32.Section flag3;
#endregion
#region Properties
/// <summary>
/// Flag 1
/// </summary>
public int Flag1
{
get { return info[flag1]; }
set { info[flag1] = value; }
}
/// <summary>
/// Flag 2
/// </summary>
public bool Flag2
{
get { return info[flag2] != 0; }
set { info[flag2] = value ? 1 : 0; }
}
/// <summary>
/// Flag 1
/// </summary>
public int Flag3
{
get { return info[flag3]; }
set { info[flag3] = value; }
}
#endregion
#region ToString
/// <summary>
/// Allows us to represent this in human readable form
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"Name: {nameof(MyUnion2)}{Environment.NewLine}Flag1: {Flag1}: Flag2: {Flag2} Flag3: {Flag3} {Environment.NewLine}BitVector32: {info}{Environment.NewLine}";
}
#endregion
}
Последнее слово об этой теме ... этодолжно быть очевидно, что это должно быть сделано только в том случае, если вы абсолютно ДОЛЖНЫ сделать это.Очевидно, что для этого требуются специальные знания вашей операционной системы, языка, на котором вы работаете, вашего соглашения о вызовах и множества других хрупких требований.
В любом другом контексте здесь так много запахов кода, что это явно кричит о непереносимости.Но из контекста вашего вопроса, я бы предположил, что весь смысл в том, что вам нужно бегать близко к оборудованию и нужна такая точность.
Caveat emptor!