2016-01-07 4 views
11

Wenn P/Invoke ausgeführt wird, muss das Datenlayout übereinstimmen.Was ist die Größe und Ausrichtung von C# fixed Bool Array in struct?

Wir können das Layout der Struktur mit einem Attribut steuern.

Zum Beispiel:

struct MyStruct 
{ 
    public bool f; 
} 

gibt eine Größe von 4. Während wir Compiler kann sagen, es ist ein 1-Byte-Bool übereinstimmen C++ Art von bool zu machen:

struct MyStruct 
{ 
    [MarshalAs(UnmanagedType.I1)] 
    public bool f; 
} 

gibt eine Größe von 1.

Diese machen Sinn. Aber als ich ein festes Bool-Array getestet habe, war ich verwirrt.

unsafe struct MyStruct 
{ 
    public fixed bool fs[1]; 
} 

gibt eine Größe von 4 Bytes. und

unsafe struct MyStruct 
{ 
    public fixed bool fs[4]; 
} 

gibt immer noch eine Größe von 4 Bytes. aber

unsafe struct MyStruct 
{ 
    public fixed bool fs[5]; 
} 

mit einer Größe von 8 gibt

es in fester bool Array sieht, ist die Größe des bool Elements noch 1 Byte, sondern die Ausrichtung beträgt 4 Byte. Dies stimmt nicht mit dem C++ - Bool-Array überein, bei dem es sich um 1 Byte Größe und Ausrichtung handelt.

Kann mir jemand das erklären?

Update: Ich endlich herausfinden, der Grund ist, bool Typ in einer Struktur, dann wird diese Struktur nie blitable! Erwarten Sie also nicht, dass eine Struktur mit Bool-Typ das gleiche Layout wie in C hat.

Grüße, Xiang.

Antwort

13

Ein bool ist ziemlich speziell, es geht zurück auf Dennis Ritchies Entscheidung, der C-Sprache keinen Bool-Typ zu geben. Das hat viele Chaos-, Sprach- und Betriebssystem-Designer verursacht, die es selbst hinzugefügt und inkompatible Entscheidungen getroffen haben.

Es wurde dem Winapi als BOOL Typedef hinzugefügt. Das ist das Standard-Marshalling, wenn Sie keinen anderen Typ erzwingen. Typedef-ed als int, um es kompatibel mit C zu halten, dauert 4 Bytes, wie Sie herausgefunden haben. Und richtet sich an 4, wie Sie herausgefunden haben, wie jeder Int tut.

Es wurde zu C++ hinzugefügt. Ohne eine Größenangabe wählen die meisten C++ - Compiler-Implementierungen ein einzelnes Byte für den Speicher aus. Vor allem der Microsoft C++ Compiler hat die wahrscheinlichste Implementierung durchgeführt, mit der Sie interagieren werden.

Es wurde als VARIANT_BOOL zu COM Automation hinzugefügt. Ursprünglich als das neue Erweiterungsmodell für Visual Basic gedacht, um die VBX-Einschränkungen loszuwerden, wurde es sehr populär und fast jede Sprachlaufzeit unter Windows unterstützt es jetzt. VB war damals stark von 16-Bit-Betriebssystem-Sensibilitäten betroffen, ein VARIANT_BOOL benötigt 2 Bytes.

Alle drei nativen Laufzeitumgebungen sind wahrscheinlich Ziele für Interop in einem C# -Programm. Klar, die CLR-Designer hatten eine sehr schwierige Wahl, da sie zwischen 1, 2 und 4 Bytes wählen mussten.Es gibt keine Möglichkeit zu gewinnen, während die CLR versucht, auf COM-Interop zu raten. Sie kann nicht wissen, ob Sie versuchen, mit einem C-basierten API- oder einem C++ - Programm zu interagieren. Also haben sie die einzig logische Wahl getroffen: keine von ihnen.

Eine Struktur oder Klassenart, die eine Bool enthält, ist nie blitable. Nicht einmal, wenn Sie [MarshalAs (UnmanagedType.U1)] anwenden, die es mit dem CLR-Typ kompatibel machen würde. Nicht so sicher, dass das eine gute Entscheidung war, aber es war die, die sie gemacht haben, damit wir uns damit befassen müssen.

Es ist sehr wünschenswert, eine blitable Struktur zu erhalten, die das Kopieren vermeidet. Dadurch kann systemeigener Code direkt auf den verwalteten Heap und Stack zugreifen. Ziemlich gefährlich und viele eine defekte Pinvoke-Deklaration hat den GC-Heap ohne den üblichen Vorteil der unsicheren Keyword-Warnung beschädigt. Aber unmöglich für Geschwindigkeit zu schlagen.

Sie erhalten eine blitable Struktur von nicht mit bool. Verwenden Sie stattdessen byte. Sie können das Bool immer noch zurückholen, indem Sie das Strukturelement mit einer Eigenschaft umhüllen. Verwenden Sie keine automatisch implementierte Eigenschaft, Sie müssen sich um die Position des Bytes kümmern. Also:

struct MyStruct 
{ 
    private byte _f; 
    public bool f { 
     get { return _f != 0; } 
     set { _f = value ? 1 : 0; } 
    } 
} 

Native Code ist unmerklich für die Eigenschaft. Machen Sie sich keine Gedanken über den Laufzeit-Overhead für den Getter und Setter, der Jitter-Optimierer macht sie unsichtbar und sie werden zu einer einzigen CPU-Anweisung.

+0

'privaten Bool _f;' -> 'privaten _f Byte;'? – PetSerAl

+0

Froh, dass ich es gut erklärt habe :) Danke. –

+0

Danke, das war eine sehr detaillierte Erklärung. Obwohl ich verstehe, eine Eigenschaft in C++ könnte eine robuste Lösung sein, aber ich möchte immer noch wissen, warum in C# Bool Array fest, es Verhalten sehr seltsam, sieht aus wie seine Größe ist 1 Byte, aber seine Ausrichtung ist 4 Bytes. Wissen Sie, welche Magie hinter dem C# Marshalling auf dem in der Struktur eingebetteten Bool-Array steckt? –

-1

Sollte funktionieren:

[StructLayout(LayoutKind.Sequential)] 
unsafe struct MyStruct 
{ 
    public fixed bool fs[5]; 
} 
+0

Ich habe getestet, auch ich habe dieses sequentielle Layout-Attribut hinzugefügt, Bool [3] Größe = 4, Bool [4] Größe = 4, Bool [5] Größe = 8, gleich. –

+0

Ich auch :) Haben Sie versucht [StructLayout (LayoutKind.Sequential, Pack = 1)]? Sieht aus wie irgendwo im Projekt haben Sie eine Option, die die Standardausrichtung für Strukturen auf 4 Bytes setzt – Dmitry