Использует неожиданное поведение FieldOffset
Я пытаюсь понять явное расположение структуры и наложение структуры, и я не вижу поведения, которое я ожидаю. Учитывая код ниже:
class Program
{
static void Main(string[] args)
{
byte[] bytes = new byte[17];
bytes[0] = 0x01; // Age is 1 //IntField1
bytes[1] = 0x00; //IntField1
bytes[2] = 0x00; //IntField1
bytes[3] = 0x00; //IntField1
bytes[4] = 0x02; //IntField2
bytes[5] = 0x00; //IntField2
bytes[6] = 0x00; //IntField2
bytes[7] = 0x00; //IntField2
bytes[8] = 0x41; //CharArray A
bytes[9] = 0x42; //CharArray B
bytes[10] = 0x43; //CharArray C
bytes[11] = 0x44; //CharArray D
bytes[12] = 0x45; //CharArray E
bytes[13] = 0x46; //CharArray F
bytes[14] = 0x00; // \0 decimal 0
bytes[15] = 0x00; // \0 decimal 0
bytes[16] = 0x01; // 1 decimal 1
Console.WriteLine(Marshal.SizeOf(typeof(TestStruct)));
TestStruct testStruct2 = (TestStruct) RawDeserialize(bytes, 0, typeof (TestStruct));
Console.WriteLine(testStruct2);
Console.ReadLine();
}
public static object RawDeserialize( byte[] rawData, int position, Type anyType )
{
int rawsize = Marshal.SizeOf( anyType );
if( rawsize > rawData.Length )
return null;
IntPtr buffer = Marshal.AllocHGlobal( rawsize );
Marshal.Copy( rawData, position, buffer, rawsize );
object retobj = Marshal.PtrToStructure( buffer, anyType );
Marshal.FreeHGlobal( buffer );
return retobj;
}
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct
{
[FieldOffset(0)]
public int IntField1;
[FieldOffset(4)]
public int IntField2;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] CharArray;
[FieldOffset(16)]
public byte SomeByte;
[FieldOffset(8)]
public TestStruct2 SubStruct;
public override string ToString()
{
return string.Format("IntField1: {0}\nIntField2: {1}\nCharArray: {2}\nSomeByte: {3}\nSubStruct:\n{{{4}}}",
IntField1, IntField2, new string(CharArray), SomeByte, SubStruct);
}
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct2
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] CharArray1;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public char[] CharArray2;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public char[] CharArray3;
public override string ToString()
{
return string.Format("CharArray1: {0}\nCharArray2: {1}\nCharArray3: {2}",
new string(CharArray1), new string(CharArray2), new string(CharArray3));
}
}
Я ожидаю, что результат от этого будет что-то вроде:
IntField1: 1
IntField2: 2
CharArray: ABCDEF
SomeByte: 1
SubStruct:
{CharArray1: ABCDEF
CharArray2: ABCD
CharArray3: E}
но результат таков:
IntField1: 1
IntField2: 2
CharArray: ABCD
SomeByte: 1
SubStruct:
{CharArray1: ABCD
CharArray2: ABCD
CharArray3: EF}
Почему CharArray в TestStruct имеет длину 4? Я предположил, что в нем должно быть 6 символов ABCDEF, но он содержит только ABCD. То же самое для TestStruct2.CharArray1.
3 ответа
Помещая TestStruct2 после CharArray, но с тем же смещением, теперь указатель на CharArray2 TestStruct2 перезаписывает то, что раньше было указателем на собственный chararray TestStruct.
Если вы закомментируете или измените длину CharArray2 в TestStruct2, вы увидите ожидаемые результаты.
Точно так же смотрите, что происходит, когда вы сначала ставите struct2, то есть:
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct
{
[FieldOffset(8)]
public TestStruct2 SubStruct;
[FieldOffset(0)]
public int IntField1;
[FieldOffset(4)]
public int IntField2;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] CharArray;
[FieldOffset(16)]
public byte SomeByte;
public override string ToString()
{
return string.Format("IntField1: {0}\nIntField2: {1}\nCharArray: {2}\nSomeByte: {3}\nSubStruct:\n{{{4}}}",
IntField1, IntField2, new string(CharArray), SomeByte, SubStruct);
}
}
Теперь эффект полностью изменен, и CharArray2 TestStruct2 имеет длину шесть символов.
char[] является ссылочным типом, его размер равен одному IntPtr, который может быть 4 или 8 байт - в зависимости от платформы (x86 или x64) и его значение не хранится на месте внутри структуры.
Атрибут MarshalAs не меняет способ хранения информации в структуре, а только способ ее перевода (например, в / из неуправляемого кода).
Следует иметь в виду, что char в C# - это 2-байтовый символ Unicode.
Хотя я так и не смог получить ожидаемый результат, пока держу там подструктуру. Перекрывающиеся массивы SizeConst, похоже, все портят.