.NET值I型具有以下.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
我然後有一些測試代碼: 的foreach(在對StringPair對) { 日期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(0×20)以及V字段偏移量24(0x38-0x20 )。 JIT已經改變了周圍的秩序。 Visual Studio調試器也顯示了這個倒序。
爲什麼?我一直在拉我的頭髮,試圖看看我的邏輯出錯了。如果我在結構中交換D和V的順序,那麼一切都可以正常工作,但是這段代碼需要能夠處理其他開發人員定義結構的插件體系結構,並且他們不能期望記住神祕的佈局規則。
謝謝 - 我錯過了Marshall。*方法只應用於編組指針的事實。出於性能原因,我希望避免額外的數據副本,但是如果我想支持任意結構 – 2009-12-17 14:03:14
+1來澄清JIT結構佈局是與CLR指定的完全分離的問題,那看起來是不可避免的,因爲 - 原則上 - 它不能被管理程序看到,因此也不會受到影響。我已經注意到JITer似乎在非託管之前放置託管字段。 – 2010-12-07 22:09:56
值類型(的實例)的內存中的確切位佈局可能與決定是否實現IEquatable(以及object.Equals和GetHashCode的推薦覆蓋)有一定關聯性。如果你沒有實現IEquatable,那麼我相信默認生成的代碼會對內存中的位進行按位比較。如果佈局中存在GAPS,那麼您可能會浪費時間來比較那些無關緊要的位(在C++中,您可能還冒着正確性,因爲這些位可能未被初始化 - 但我認爲它們始終位於C#中)。瞭解佈局會告知決定要實施什麼。 –
2011-05-06 17:03:46