Я наконец-то нашел способ, который не подразумевал рефлексию и в основном удобен для пользователя. Он использует класс DataConverter Mono ( source ), который, к сожалению, на данный момент довольно глючный. (Например, значения типа float и double не работают должным образом, синтаксический анализ строки не работает и т. Д.)
Хитрость заключается в том, чтобы распаковать и повторно упаковать байты как big-endian, для чего требуется строка, описывающая, какие типы находятся в байтовом массиве (см. Последний метод). Также, выравнивание байтов является сложным: в байтах четыре структура вместо одного, потому что маршалинг, кажется, полагается на 4-байтовое выравнивание (я все еще не совсем понимаю эту часть). (РЕДАКТИРОВАТЬ: я обнаружил, что добавление Pack=1
к атрибуту StructLayout
обычно решает проблемы с выравниванием байтов.)
Обратите внимание, этот пример кода использовался в LINQPad - метод расширения Dump просто печатает информацию об объекте и возвращает объект (он свободно).
public void Main()
{
var beBytes = new byte[] {
0x80,
0x80,
0x80,
0x80,
0x80,0,
0x80,0,
0x80,0,0,0,
0x80,0,0,0,
0x80,0,0,0,0,0,0,0,
0x80,0,0,0,0,0,0,0,
// 0,0,0x80,0x3F, // float of 1
// 0,0,0,0,0,0,0xF0,0x3F, // double of 1
0x54,0x65,0x73,0x74,0x69,0x6E,0x67,0,0,0 // Testing\0\0\0
};
var leBytes = new byte[] {
0x80,
0x80,
0x80,
0x80,
0,0x80,
0,0x80,
0,0,0,0x80,
0,0,0,0x80,
0,0,0,0,0,0,0,0x80,
0,0,0,0,0,0,0,0x80,
// 0,0,0x80,0x3F, // float of 1
// 0,0,0,0,0,0,0xF0,0x3F, // double of 1
0x54,0x65,0x73,0x74,0x69,0x6E,0x67,0,0,0 // Testing\0\0\0
};
Foo fooLe = ByteArrayToStructure<Foo>(leBytes).Dump("LE");
Foo fooBe = ByteArrayToStructureBigEndian<Foo>(beBytes,
"bbbbsSiIlL"
// + "fd" // float, then double
+"9bb").Dump("BE");
Assert.AreEqual(fooLe, fooBe);
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Foo {
public byte b1;
public byte b2;
public byte b3;
public byte b4;
public short s;
public ushort S;
public int i;
public uint I;
public long l;
public ulong L;
// public float f;
// public double d;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string MyString;
}
T ByteArrayToStructure<T>(byte[] bytes) where T: struct
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(T));
handle.Free();
return stuff;
}
T ByteArrayToStructureBigEndian<T>(byte[] bytes, string description) where T: struct
{
byte[] buffer = bytes;
IList unpacked = DataConverter.Unpack("^"+description, buffer, 0).Dump("unpacked");
buffer = DataConverter.PackEnumerable("!"+description, unpacked).Dump("packed");
return ByteArrayToStructure<T>(buffer);
}