C# 带有LayoutKind.Explicit的布尔编组,这是否已损坏或按设计失败?
首先,据说布尔类型具有四字节值的默认封送处理类型。因此,以下代码起作用:C# 带有LayoutKind.Explicit的布尔编组,这是否已损坏或按设计失败?,c#,pinvoke,boolean,marshalling,layoutkind.explicit,C#,Pinvoke,Boolean,Marshalling,Layoutkind.explicit,首先,据说布尔类型具有四字节值的默认封送处理类型。因此,以下代码起作用: struct A { public bool bValue1; public int iValue2; } struct B { public int iValue1; public bool bValue2; } public static void Main() {
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(可能是由于重叠布尔)
那么问题是:这是一个bug,还是只是按照设计失败了?
背景:这来自
更新:当您使用大整数值(>255)时,这就更奇怪了,因为只有用于布尔值的字节被修改为1,从而将b.bValue2的0x0f00更改为0x0f01。对于上面的a.bValue1,它根本不会被转换,0x0f00为a.bValue1提供了一个假值
更新#2:
上述问题最明显、最合理的解决方案是使用uint进行编组,并公开布尔属性。用“变通办法”真正解决问题是毫无疑问的。我最想知道的是,这是一个bug还是你期望的行为
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; } }
}
注释为“FAILS,WOW,WTF?”的行由于执行布尔比较的方式而失败。它将2与1进行比较:
IL_007e: ldc.i4.1
IL_007f: ldloca.s 3
IL_0081: ldflda valuetype Test/A Test/Broken::a
IL_0086: ldfld bool Test/A::bValue1
IL_008b: ceq
ceq最后将1与bValue中的字节(即2)进行比较
有趣的是if(breaked.a.bValue1)将测试'true',因为它不是零
至于另一个问题(breaked.a.iValue2==4),当我申请时,它消失了:
[MarshalAs (UnmanagedType.Bool)]
到结构中的两个布尔字段。这确保布尔值被封送为整数(在.NET中为4字节)。它按设计工作 以下是正在发生的事情: 取新的int[]{2,4},让我们将其编组为A、B、breaked和brokern2。 最后一个与break相同,但字段的顺序相反(先是b,然后是a) 如果我们将int封送到这些结构中,我们将在内存中获得以下值:
- A:1,4
- B:2,1
- 坏掉的:2,1
- 经纪人2:1,4
- 当封送拆收器遇到布尔值时,其值为:bool=(original!=0)李>
- 当有两个字段映射到同一内存中时,最后一个字段的规则获胜
对于breake,由于B是最后一个字段,因此其规则适用,因此第二个int被转换为1。类似地,对于brokern2.它似乎是正确的,因为添加了另一个int结构:
struct C
{
public int iValue1;
public int iValue2;
}
到最后,工会似乎至少纠正了部分问题。然而,这仍然是有缺陷的,因为布尔意志只考虑一个字节值,并证明是不可靠的。最后,我提出的最佳答案是使用自定义类型进行封送处理
[Serializable]
[ComVisible(true)]
public struct BOOL : IComparable, IConvertible, IComparable<BOOL>, IEquatable<BOOL>, IComparable<bool>, IEquatable<bool>
{
private uint _data;
public BOOL(bool value) { _data = value ? 1u : 0u; }
public BOOL(int value) { _data = unchecked((uint)value); }
public BOOL(uint value) { _data = value; }
private bool Value { get { return _data != 0; } }
private IConvertible Convertible { get { return _data != 0; } }
#region IComparable Members
public int CompareTo(object obj) { return Value.CompareTo(obj); }
#endregion
#region IConvertible Members
public TypeCode GetTypeCode() { return Value.GetTypeCode(); }
public string ToString(IFormatProvider provider) { return Value.ToString(provider); }
bool IConvertible.ToBoolean(IFormatProvider provider) { return Convertible.ToBoolean(provider); }
byte IConvertible.ToByte(IFormatProvider provider) { return Convertible.ToByte(provider); }
char IConvertible.ToChar(IFormatProvider provider) { return Convertible.ToChar(provider); }
DateTime IConvertible.ToDateTime(IFormatProvider provider) { return Convertible.ToDateTime(provider); }
decimal IConvertible.ToDecimal(IFormatProvider provider) { return Convertible.ToDecimal(provider); }
double IConvertible.ToDouble(IFormatProvider provider) { return Convertible.ToDouble(provider); }
short IConvertible.ToInt16(IFormatProvider provider) { return Convertible.ToInt16(provider); }
int IConvertible.ToInt32(IFormatProvider provider) { return Convertible.ToInt32(provider); }
long IConvertible.ToInt64(IFormatProvider provider) { return Convertible.ToInt64(provider); }
sbyte IConvertible.ToSByte(IFormatProvider provider) { return Convertible.ToSByte(provider); }
float IConvertible.ToSingle(IFormatProvider provider) { return Convertible.ToSingle(provider); }
ushort IConvertible.ToUInt16(IFormatProvider provider) { return Convertible.ToUInt16(provider); }
uint IConvertible.ToUInt32(IFormatProvider provider) { return Convertible.ToUInt32(provider); }
ulong IConvertible.ToUInt64(IFormatProvider provider) { return Convertible.ToUInt64(provider); }
object IConvertible.ToType(Type conversionType, IFormatProvider provider) { return Convertible.ToType(conversionType, provider); }
#endregion
#region IComparable<bool> Members
public int CompareTo(BOOL other) { return Value.CompareTo(other.Value); }
public int CompareTo(bool other) { return Value.CompareTo(other); }
#endregion
#region IEquatable<bool> Members
public bool Equals(BOOL other) { return Value.Equals(other.Value); }
public bool Equals(bool other) { return Value.Equals(other); }
#endregion
#region Object Override
public override string ToString() { return Value.ToString(); }
public override int GetHashCode() { return Value.GetHashCode(); }
public override bool Equals(object obj) { return Value.Equals(obj); }
#endregion
#region implicit/explicit cast operators
public static implicit operator bool(BOOL value) { return value.Value; }
public static implicit operator BOOL(bool value) { return new BOOL(value); }
public static explicit operator int(BOOL value) { return unchecked((int)value._data); }
public static explicit operator BOOL(int value) { return new BOOL(value); }
public static explicit operator uint(BOOL value) { return value._data; }
public static explicit operator BOOL(uint value) { return new BOOL(value); }
#endregion
#region +, -, !, ~, ++, --, true, false unary operators overloaded.
public static BOOL operator !(BOOL b) { return new BOOL(!b.Value); }
public static bool operator true(BOOL b) { return b.Value; }
public static bool operator false(BOOL b) { return !b.Value; }
#endregion
#region +, -, *, /, %, &, |, ^, <<, >> binary operators overloaded.
public static BOOL operator &(BOOL b1, BOOL b2) { return new BOOL(b1.Value & b2.Value); }
public static BOOL operator |(BOOL b1, BOOL b2) { return new BOOL(b1.Value | b2.Value); }
#endregion
#region ==, !=, <, >, <=, >= comparison operators overloaded
public static bool operator ==(BOOL b1, BOOL b2) { return (b1.Value == b2.Value); }
public static bool operator !=(BOOL b1, BOOL b2) { return (b1.Value != b2.Value); }
#endregion
}
[可序列化]
[ComVisible(true)]
公共结构布尔:IComparable,IConvertible,IComparable,IEquatable,IComparable,IEquatable
{
私有uint_数据;
公共布尔(布尔值){u data=value?1u:0u;}
公共BOOL(int值){u data=unchecked((uint)值);}
公共布尔(uint值){u data=value;}
私有布尔值{get{return_data!=0;}}
私有IConvertible可转换{get{return\u data!=0;}
#区域i可比较成员
public int CompareTo(object obj){返回值.CompareTo(obj);}
#端区
#区域IConvertible成员
公共类型代码GetTypeCode(){返回值.GetTypeCode();}
公共字符串ToString(IFormatProvider提供程序){返回值.ToString(提供程序);}
bool IConvertible.ToBoolean(IFormatProvider提供程序){return Convertible.ToBoolean(提供程序);}
字节IConvertible.ToByte(IFormatProvider提供程序){return Convertible.ToByte(提供程序);}
char IConvertible.ToChar(IFormatProvider提供程序){return Convertible.ToChar(提供程序);}
DateTime IConvertible.ToDateTime(IFormatProvider提供程序){return Convertible.ToDateTime(提供程序);}
十进制IConvertible.ToDecimal(IFormatProvider提供程序){return Convertible.ToDecimal(提供程序);}
double IConvertible.ToDouble(IFormatProvider提供程序){return Convertible.ToDouble(提供程序);}
短IConvertible.ToInt16(IFormatProvider提供程序){return Convertible.ToInt16(提供程序);}
int IConvertible.ToInt32(IFormatProvider提供程序){return Convertible.ToInt32(提供程序);}
long IConvertible.ToInt64(IFormatProvider提供程序){return Convertible.ToInt64(提供程序);}
sbyte IConvertible.ToSByte(IFormatProvider提供程序){return Convertible.ToSByte(提供程序);}
浮点IConvertible.ToSingle(IFormatProvider提供程序){return Convertible.ToSingle(提供程序);}
ushort-IConvertible.ToUInt16(IFormatProvider提供程序){return-Convertible.ToUInt16(提供程序);}
uint IConvertible.ToUInt32(IFormatProvider提供程序){return Convertible.ToUInt32(提供程序);}
ulong IConvertible.ToUInt64(IFormatProvider提供程序){return Convertible.ToUInt64(提供程序);}
对象IConvertible.ToType(类型转换类型,IFormatProvider提供程序){return Convertible.ToType(转换类型,提供程序);}
#端区
#区域i可比较成员
public int CompareTo(BOOL other){返回值.CompareTo(other.Value);}
public int CompareTo(bool-other){返回值.CompareTo(other);}
#端区
#区域可容纳成员
公共bool Equals(bool other){返回值.Equals(other.Value);}
公共bool Equals(bool other){返回值.Equals(other);