У меня есть следующие типы значений .NET:Компоновка типа значения .NET в памяти
[StructLayout(LayoutKind.Sequential)]
public struct Date
{
public UInt16 V;
}
[StructLayout(LayoutKind.Sequential)]
public struct StringPair
{
public String A;
public String B;
public String C;
public Date D;
public double V;
}
У меня есть код, который передается указатель на тип значения для неуправляемого кода, наряду со смещениями, обнаруженных с помощью вызова системы .Runtime.InteropServices.Marshal.OffsetOf. Неуправляемый код заполняет дату и двойные значения.
Смещения, о которых сообщается в StringPair структуры именно то, что я хотел бы ожидать: 0, 8, 16, 24, 32
У меня есть следующий код в тестовой функции:
FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public);
for (int i = 0; i < fields.Length; i++)
{
int offset = System.Runtime.InteropServices.Marshal.OffsetOf(typeof(StringPair), fields[i].Name).ToInt32();
Console.WriteLine(String.Format(" >> field {0} @ offset {1}", fields[i].Name, offset));
}
Который печатает именно эти смещения.
>> field A @ offset 0
>> field B @ offset 8
>> field C @ offset 16
>> field D @ offset 24
>> field V @ offset 32
Я тогда иметь некоторый тестовый код: Еогеаспа (StringPair пару в паре) { Date D = pair.D; double v = pair.V; ...
которая имеет следующий ассемблер, связанный с ним в отладчик:
Date d = pair.D;
0000035d lea rax,[rbp+20h]
00000361 add rax,20h
00000367 mov ax,word ptr [rax]
0000036a mov word ptr [rbp+000000A8h],ax
00000371 movzx eax,word ptr [rbp+000000A8h]
00000378 mov word ptr [rbp+48h],ax
double v = pair.V;
0000037c movsd xmm0,mmword ptr [rbp+38h]
00000381 movsd mmword ptr [rbp+50h],xmm0
Это загружается поле D по смещению 32 (0x20) и поле V на смещение 24 (0x38-0x20). JIT изменил порядок вокруг. Отладчик Visual Studio также показывает этот инвертированный порядок.
Почему ?? Я тянул свои волосы, пытаясь понять, где моя логика идет не так. Если я поменяю порядок D и V в структуре, тогда все будет работать, но этот код должен иметь дело с архитектурой плагина, где другие разработчики определили структуру, и от них нельзя ожидать, чтобы они запоминали тайные правила макета.
Спасибо - я упустил тот факт, что методы Маршалла. * Применимы только к маршалированному указателю. По соображениям производительности я надеялся избежать дополнительной копии данных, но это выглядит довольно неустранимым, если я хочу поддерживать произвольные структуры –
+1, чтобы уточнить, что структура структуры JIT является полностью отдельной проблемой, чем то, что указывает CLR, в принципе - он не может быть замечен - и, следовательно, не имеет значения для управляемых программ. Я заметил, что JITER, кажется, помещает управляемые поля перед неуправляемым. –
Точная компоновка битов в памяти (экземпляра) типа значения может иметь некоторую релевантность для принятия решения о том, следует ли реализовать IEquatable (и рекомендуемые переопределения объекта.Equals и GetHashCode). Если вы не реализуете IEquatable, то я считаю, что генерируемый по умолчанию код выполняет побитовое сравнение по битам в памяти. Если в макете есть GAPS, вы можете тратить время на сравнение битов, которые не имеют значения (в C++ вы также можете рисковать корректностью, так как эти биты могут не инициализироваться, но я думаю, что они всегда находятся на C#). Знание макета сообщает решение о том, что реализовать. –