Wenn Sie structs innerhalb anderer structs platzieren wollen, die ihrerseits Layoutind.Explict sind, sollten Sie einen expliziten Size-Wert (in Bytes) verwenden, wenn Sie erwarten, dass sie in verschiedenen Bitness-Modi funktionieren (oder auf Maschinen mit unterschiedlichen Packing-Anforderungen) Was Sie damit sagen wollen, ist: "Legen Sie die Dinge sequentiell an und packen Sie nicht intern, sondern verwenden Sie am Ende so viel Platz wie Sie wollen". Wenn Sie die Größe nicht angeben, kann die Laufzeitumgebung so viel Platz hinzufügen, wie sie will.
Der Grund für die generelle Weigerung, Strukturen und Objekttypen überlappen zu lassen, ist, dass die GC-Routine die Freiheit haben muss, den lebenden Objektgraphen zu durchlaufen. Dabei kann sie nicht wissen, ob ein vereinigtes (überlappendes) Feld als Objektreferenz oder als rohe Bits (z.B. ein int oder ein float) sinnvoll ist. Da sie debe alle lebenden Objektreferenzen durchlaufen, um sich korrekt zu verhalten, würde es damit enden, dass "zufällige" Bits durchlaufen werden, die irgendwo im Heap (oder aus ihm heraus) zeigen könnten, als ob sie Referenzen wären, und bevor man sich versieht, hat man einen General Protection Fault.
Da 32/64-Referenzen je nach Laufzeit 32 oder 64 Bits belegen, müssen Sie Explict verwenden, nur Referenzen mit Referenzen und Werttypen mit Werttypen vereinigen, sicherstellen, dass Ihre Referenztypen an den Grenzen der beiden Zielplattformen ausgerichtet sind, falls sie sich unterscheiden (Hinweis: Laufzeitabhängig, siehe unten), und eine der folgenden Maßnahmen ergreifen:
- Stellen Sie sicher, dass alle Referenzfelder der letzte Eintrag in der Struktur sind - es steht dann frei, die Struktur je nach der Bitdichte der Laufzeitumgebung zu vergrößern oder zu verkleinern.
- Erzwingen Sie, dass alle Objektreferenzen 64 Bit verbrauchen, unabhängig davon, ob Sie in einer 32- oder 64-Bit-Umgebung arbeiten.
Hinweis zur Ausrichtung: Entschuldigung Ich habe mich bei den nicht ausgerichteten Referenzfeldern geirrt - der Compiler hat die Typladung entfernt, es sei denn, ich habe irgendeine Aktion mit der Struktur durchgeführt.
[StructLayout(LayoutKind.Explicit)]
public struct Foo
{
[FieldOffset(0)]
public byte padding;
[FieldOffset(1)]
public string InvalidReference;
}
public static void RunSnippet()
{
Foo foo;
foo.padding = 0;
foo.ValidReference = "blah";
// Console.WriteLine(foo); // uncomment this to fail
}
Die entsprechenden Details finden Sie in der ECMA-Spezifikation http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf siehe Abschnitt 16.6.2, in dem die Ausrichtung von Werten nativer Größe einschließlich & vorgeschrieben ist. Es wird darauf hingewiesen, dass die Anweisung "unaligned prefix" existiert, um dies bei Bedarf zu umgehen.
Auf mono jedoch (sowohl OSX intel als auch Win32 intel 32 bit) funktioniert der obige Code. Entweder respektiert die Laufzeitumgebung die Layouts nicht und "korrigiert" die Dinge stillschweigend, oder sie erlaubt eine willkürliche Ausrichtung (in der Vergangenheit war sie in dieser Hinsicht weniger flexibel als die MS-Laufzeitumgebung, was überraschend ist). Die von mono erzeugte CLI-Zwischenform enthält keine .unaligned-Befehlspräfixe und scheint daher nicht mit der Spezifikation übereinzustimmen.
Das wird mich lehren, nur noch bei Mono nachzusehen.
0 Stimmen
Beachten Sie, dass char in .NET 16-Bit ist. blah und blah2 überschneiden sich teilweise, ebenso wie blah2 und blah3.