我知道为了在C#中表示联合,我需要使用StructLayoutLayoutKind.Explicit)和FieldOffset(x)属性来指定联合中的字节偏移量。但是,我有一个以下联合,我想表示和FieldOffset属性只偏移一个字节的大小。
union _myUnion
{
unsigned int info;
struct
{
unsigned int flag1:1 // bit 0
unsigned int flag2:1 // bit 1
unsigned int flag3:1 // bit 2
unsigned int flag4:1 // bit 3
unsigned int flag5:1 // bit 4
unsigned int flag6:1 // bit 5
.
.
.
unsigned int flag31:1 // bit 31
}
}正如您在联合中看到的内部结构一样,我不能使用FieldOffset,因为我需要一些可以稍微偏移的东西。
对此有解决方案吗?我正在尝试调用一个DLL函数,其中一个数据结构就是这样定义的,我没有办法最好地表示这个联合结构。
发布于 2011-01-29 01:15:38
那里不需要联合;一个用于数据的field+property,8个执行按位“移位”操作的属性,例如:
public uint Value {get;set;}
public uint Flag2 {
get { return Value >> 2; }
}等等。我还以为你想要bool在这里?
通常我会说:不要做可变的结构。PInvoke可能(我不确定)是一个有效的场景,所以我将忽略它:)
如果该值确实使用了超过32位的值,请考虑将支持字段切换为ulong。
发布于 2018-11-24 14:30:06
是的,你可以做到。您走在正确的道路上,答案在于使用BitVector32以及FieldOffset和StructLayout属性。但是,在执行此操作时,您需要记住以下几点:
我将根据您的问题和标志0-31的说明,假设整数大小为32。
那么诀窍就是确保以下几点:
下面是我们实现这一点的方法:
[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
}关于这个话题的最后一句话。这应该是显而易见的,只有当你绝对做这件事的时候才应该这样做。显然,这需要您的操作系统环境、您运行的语言、调用约定以及许多其他脆弱需求的专业知识。
在任何其他上下文中,这里都有太多的代码味道,这显然是不可移植性的尖叫。但从你的问题的背景来看,我猜测整个问题的要点是,你需要接近硬件并需要这种精确度。
买者自负!
发布于 2018-11-25 03:01:16
最好的解决方案是使用标志枚举
最好使用uint而不是int
[Flags]
public enum MyEnum
: uint
{
None=0,
Flag1=1,
Flag2=1<<1,
Flag3=1<<2,
// etc
Flag32=1<<31
}之后,您可以使用int作为枚举和uint
MyEnum value=MyEnum.Flag1|MyEnum.Flag2;
uint uintValue=(uint)value;
// uintValue=3这通常由PInvoke进行封送处理
https://stackoverflow.com/questions/4830978
复制相似问题