Прежде всего, говорят, что булев тип имеет маршальный тип по умолчанию с четырехбайтовым значением. Так работает следующий код:
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 (предположительно из-за перекрывающегося логического выражения).
Итак, вопрос: это ошибка, или просто не удалось, как задумано?
Справочная информация: это произошло от В чем разница между структурами, содержащими bool и uint, при использовании PInvoke?
Обновление: это даже странно, когда вы используете большие целочисленные значения (> 255), поскольку только байт, который используется для логического значения, изменяется на 1, таким образом изменяя 0x0f00 на 0x0f01 для b.bValue2. Для 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; } }
}