首先,Boolean 类型据说有一个四字节值的默认 marshal 类型。所以下面的代码有效:
struct A
{
public bool bValue1;
public int iValue2;
}
struct B
{
public int iValue1;
public bool bValue2;
}
public static void Main()
{
int[] rawvalues = new int[] { 2, 4 };
A a = (A)Marshal.PtrToStructure(GCHandle.Alloc(rawvalues, GCHandleType.Pinned).AddrOfPinnedObject(), typeof(A));
Assert.IsTrue(a.bValue1 == true);
Assert.IsTrue(a.iValue2 == 4);
B b = (B)Marshal.PtrToStructure(GCHandle.Alloc(rawvalues, GCHandleType.Pinned).AddrOfPinnedObject(), typeof(B));
Assert.IsTrue(b.iValue1 == 2);
Assert.IsTrue(b.bValue2 == true);
}
显然,这些结构独立编组就好了。这些值按预期翻译。然而,当我们通过像这样声明 LayoutKind.Explicit 将这些结构组合成一个“联合”时:
[StructLayout(LayoutKind.Explicit)]
struct Broken
{
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public B b;
}
我们突然发现自己无法正确编组这些类型。这是上述结构的测试代码以及它是如何失败的:
int[] rawvalues = new int[] { 2, 4 };
Broken broken = (Broken)Marshal.PtrToStructure(GCHandle.Alloc(rawvalues, GCHandleType.Pinned).AddrOfPinnedObject(), typeof(Broken));
Assert.IsTrue(broken.a.bValue1 != false);// pass, not false
Assert.IsTrue(broken.a.bValue1 == true);// pass, must be true?
Assert.IsTrue(true.Equals(broken.a.bValue1));// FAILS, WOW, WTF?
Assert.IsTrue(broken.a.iValue2 == 4);// FAILS, a.iValue1 == 1, What happened to 4?
Assert.IsTrue(broken.b.iValue1 == 2);// pass
Assert.IsTrue(broken.b.bValue2 == true);// pass
把这个表达式看成真的很幽默:(a.bValue1 != false && a.bValue1 == true && !true.Equals(a.bValue1))
当然,这里更大的问题是 a.iValue2 != 4,而不是 4 已更改为 1(可能是重叠的布尔值)。
所以问题是:这是一个错误,还是按设计失败了?
背景:这来自 使用 PInvoke 时包含 bool 与 uint 的结构有什么区别?
更新:当您使用大整数值(> 255)时,这甚至更奇怪,因为只有用于布尔值的字节被修改为 1,因此将 b.bValue2 的 0x0f00 更改为 0x0f01。对于上面的 a.bValue1,它根本不翻译,0x0f00 为 a.bValue1 提供了一个错误值。
更新#2:
对上述问题最明显和最合理的解决方案是使用 uint 进行编组并改为公开布尔属性。用“变通方法”真正解决问题是没有问题的。我最想知道这是一个错误还是您所期望的行为?
struct A
{
private uint _bValue1;
public bool bValue1 { get { return _bValue1 != 0; } }
public int iValue2;
}
struct B
{
public int iValue1;
private uint _bValue2;
public bool bValue2 { get { return _bValue2 != 0; } }
}