Сама Microsoft пыталась поддерживать decimal
и выравнивание своих компонентов как можно более устойчивыми. Вы также можете увидеть это в упомянутом источнике ссылок .NET Framework:
// NOTE: Do not change the order in which these fields are declared. The
// native methods in this class rely on this particular order.
private int flags;
private int hi;
private int lo;
private int mid;
Вместе с использованием [StructLayout(LayoutKind.Sequential)]
структура точно выравнивается в памяти точно так же.
Вы получаете неправильные результаты из-за метода GetBytes
, использующего переменные, которые строят данные decimal
внутри , а не в порядке их выравнивания в самой структуре:
internal static void GetBytes(Decimal d, byte[] buffer)
{
Contract.Requires((buffer != null && buffer.Length >= 16), "[GetBytes]buffer != null && buffer.Length >= 16");
buffer[0] = (byte)d.lo;
buffer[1] = (byte)(d.lo >> 8);
buffer[2] = (byte)(d.lo >> 16);
buffer[3] = (byte)(d.lo >> 24);
buffer[4] = (byte)d.mid;
buffer[5] = (byte)(d.mid >> 8);
buffer[6] = (byte)(d.mid >> 16);
buffer[7] = (byte)(d.mid >> 24);
buffer[8] = (byte)d.hi;
buffer[9] = (byte)(d.hi >> 8);
buffer[10] = (byte)(d.hi >> 16);
buffer[11] = (byte)(d.hi >> 24);
buffer[12] = (byte)d.flags;
buffer[13] = (byte)(d.flags >> 8);
buffer[14] = (byte)(d.flags >> 16);
buffer[15] = (byte)(d.flags >> 24);
}
Мне кажется, что соответствующий разработчик .NET попытался адаптировать формат, представленный GetBytes, к little-endian, но допустил одну ошибку. Он упорядочил не только байты компонентов decimal
, но и сами компоненты. (flags, hi, lo, mid становится lo, mid, hi, flags.) Но макет с прямым порядком байтов применяется только к полям, а не к целым struct
с - особенно с [StructLayout(LayoutKind.Sequential)]
.
Мой совет здесь обычно заключается в том, чтобы использовать методы, которые Microsoft предлагает в своих классах. Поэтому я бы предпочел любой способ сериализации данных на основе GetBytes
или GetBits
, чем с помощью unsafe
, потому что Microsoft будет поддерживать совместимость с BinaryWriter
любым способом. Однако комментарии довольно серьезны, и я бы не ожидал, что Microsoft нарушит платформу .NET на этом базовом уровне.
Мне трудно поверить, что производительность имеет решающее значение для unsafe
, а не GetBits
. Ведь мы говорим о decimal
здесь. Вы все еще можете нажать int
из GetBits
через unsafe
в свой byte[]
.