Ответ 1
BinaryReader.ReadInt64
мало ориентируется по дизайну. Из документации:
BinaryReader считывает этот тип данных в формате little-endian.
Фактически, мы можем проверить источник для BinaryReader.ReadInt64
с помощью Reflector.
public virtual long ReadInt64() {
this.FillBuffer(8);
uint num = (uint) (((this.m_buffer[0] |
(this.m_buffer[1] << 0x08)) |
(this.m_buffer[2] << 0x10)) |
(this.m_buffer[3] << 0x18));
uint num2 = (uint) (((this.m_buffer[4] |
(this.m_buffer[5] << 0x08)) |
(this.m_buffer[6] << 0x10)) |
(this.m_buffer[7] << 0x18));
return (long) ((num2 << 0x20) | num);
}
Показывая, что BinaryReader.ReadInt64
читает как малое endian независимо от базовой архитектуры машины.
Теперь BitConverter.ToInt64
должен уважать законность вашей базовой машины. В Reflector мы можем видеть
public static unsafe long ToInt64(byte[] value, int startIndex) {
// argument checking elided
fixed (byte* numRef = &(value[startIndex])) {
if ((startIndex % 8) == 0) {
return *(((long*) numRef));
}
if (IsLittleEndian) {
int num = (numRef[0] << 0x00) |
(numRef[1] << 0x08) |
(numRef[2] << 0x10) |
(numRef[3] << 0x18);
int num2 = (numRef[4] << 0x00) |
(numRef[5] << 0x08) |
(numRef[6] << 0x10) |
(numRef[7] << 0x18);
return (((long) ((ulong) num)) | (num2 << 0x20));
}
int num3 = (numRef[0] << 0x18) |
(numRef[1] << 0x10) |
(numRef[2] << 0x08) |
(numRef[3] << 0x00);
int num4 = (numRef[4] << 0x18) |
(numRef[5] << 0x10) |
(numRef[6] << 0x08) |
(numRef[7] << 0x00);
return (((long) ((ulong) num4)) | (num3 << 0x20));
}
Итак, мы видим, что если startIndex
соответствует нулю по модулю восемь, то прямой трансляции выполняется из восьми байтов, начиная с адреса numRef
. Этот случай обрабатывается специально из-за проблем с выравниванием. Строка кода
return *(((long *) numRef));
переводится непосредственно на
ldloc.0 ;pushes local 0 on stack, this is numRef
conv.i ;pop top of stack, convert to native int, push onto stack
ldind.i8 ;pop address off stack, indirect load from address as long
ret ;return to caller, return value is top of stack
Итак, мы видим, что в этом случае ключ является инструкцией ldind.i8
. CLI является агностиком относительно достоверности лежащей в основе машины. Это позволяет описать компилятор JIT. На машине с маленьким концом ldind.i8
загрузит более высокие адреса в более значимые биты, а на машине большого размера ldind.i8
загрузит более высокие адреса в менее значимые байты. Следовательно, в этом случае правильность обработки выполняется корректно.
В другом случае вы можете видеть, что существует явная проверка статического свойства BitConverter.IsLittleEndian
. В случае с маленьким концом буфер интерпретируется как маленький endian (так что память { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }
интерпретируется как long 0x0706050403020100
), а в случае большого endian буфер интерпретируется как большой endian (так что память { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }
интерпретируется как длинный 0x0001020304050607
). Таким образом, для BitConverter
все сводится к контенту машины для нижнего белья. Я отмечаю, что вы на чипе Intel на Windows 7 x64. Чипы Intel мало ориентированы. Я отмечаю, что в Reflector статический конструктор для BitConverter
определяется следующим образом:
static BitConverter() {
IsLittleEndian = true;
}
Это на моем компьютере с Windows Vista x64. (Он может отличаться, скажем, на .NET CF на XBox 360.) Нет никаких оснований для того, чтобы Windows 7 x64 отличалась. Следовательно, вы уверены, что BitConverter.IsLittleEndian
есть false
? Это должно быть true
, и поэтому поведение, которое вы видите, является правильным.