2013-04-15 28 views
7

Spędziłem wiele godzin pracując z niezarządzanym kodem, a platformę wywołuję w .NET. Poniższy kod ilustruje coś, co mnie zastanawia, w jaki sposób niezarządzane dane są mapowane do zarządzanego obiektu w .NET.Mapowanie niezarządzanych danych do zarządzanej struktury w .NET

W tym przykładzie mam zamiar użyć struktury RECT:

implementacja C++ RECT (niekontrolowana Win32 API)

typedef struct _RECT { 
    LONG left; 
    LONG top; 
    LONG right; 
    LONG bottom; 
} RECT, *PRECT; 

C realizacja # RECT (zarządzany .NET/C#)

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int left, top, right, bottom; 
} 

OK, więc mój ekwiwalent C# powinien zadziałać, dobrze? Mam na myśli, że wszystkie zmienne są w tej samej kolejności co struktura C++ i używają tych samych nazw zmiennych.

Moje założenie o LayoutKind.Sequential oznacza, że ​​niezarządzane dane są mapowane do zarządzanego obiektu w tej samej kolejności, w jakiej występuje w strukturze C++. tj. dane będą mapowane, zaczynając od lewej, potem od góry, potem od prawej, potem od dołu.

Na tej podstawie powinien być w stanie zmienić mojego C# struktury ...

realizacja C# RECT (nieco czystsze)

[StructLayout(LayoutKind.Sequential)] 
public struct Rect //I've started by giving it a .NET compliant name 
{ 
    private int _left, _top, _right, _bottom; // variables are no longer directly accessible. 

    /* I can now access the coordinates via properties */ 
    public Int32 Left 
    { 
     get { return _left; } 
     set { this._left = value; } 
    } 

    public Int32 Top 
    { 
     get { return _top; } 
     set { this._top = value; } 
    } 

    public Int32 Right 
    { 
     get { return _right; } 
     set { this._right = value; } 
    } 

    public Int32 Bottom 
    { 
     get { return _bottom; } 
     set { this._bottom = value; } 
    } 
} 

Więc co się dzieje, gdy zmienne są deklarowane w złe zamówienie? Przypuszczalnie to powoduje wkręcenie współrzędnych, ponieważ nie będą one już mapowane na poprawne?

public struct RECT 
{ 
    public int top, right, bottom, left; 
} 

na oko, będzie to mapa tak:

top = lewy

prawo = top

dno = prawy

lewo = dolny

Więc moje pytanie jest po prostu, czy mam rację w moim założeniu, że mogę zmodyfikować strukturę zarządzaną pod względem specyfikatora dostępu dla każdej zmiennej, a nawet nazwy zmiennej, ale nie mogę zmienić kolejności zmiennych?

Odpowiedz

7

Jeśli naprawdę chcesz zmienić kolejność zmiennych członkowskich, możesz to zrobić za pomocą FieldOffsetAttribute. Po prostu sprawia, że ​​jest mniej czytelny.

Musisz także ustawić swój StructLayout na LayoutKind.Explicit, aby wskazać, że sam ustawiasz przesunięcia.

Przykład:

[StructLayout(LayoutKind.Explicit)] 
public struct RECT 
{ 
    [FieldOffset(4)] public int top; 
    [FieldOffset(8)] public int right; 
    [FieldOffset(12)] public int bottom; 
    [FieldOffset(0)] public int left; 
} 
+0

Doskonała odpowiedź. Zwięzłe i łatwe do zrozumienia. +1 – series0ne

+0

Naprawdę naprawdę nie chcesz tego robić. –

2

Tak, wydaje się, że twoje myślenie jest OK. StructLayout(LayoutKind.Sequential) to domyślna wartość zastosowana do C# struct, więc nie musisz tego robić.Ale jeśli chcesz mieć inną kolejność pól, możesz to zrobić, używając StructLayout(LayoutKind.Explicite), a następnie zastosować atrybuty FieldOffset do każdego pola - jest to o wiele lepsze podejście, ponieważ wyraźnie wyjaśniasz, co jest implicite i nie jesteś już zależny od czegoś, co może łatwo zmienić jak kolejność definicji pola.

Zobacz przykład MSDN: StructLayoutAttribute Class i powinno być jaśniejsze. Również - stwórz przykładową aplikację w C++ i C# i graj z nią, aby ją zrozumieć - to ci bardzo pomoże.

1

Domyślne mapowanie obiektu struct w języku C# to LayoutKind.Sequential. Zapobiega to optymalizacji pamięci przez kompilator poprzez zmianę kolejności zmiennych i zapewnia poprawne odwzorowanie.

Można jednak poinformować kompilator inną kolejność zmiennych za pomocą LayoutKind.Explicit i FieldOffsetAttribute:

[StructLayout(LayoutKind.Explicit)] 
public struct Rect 
{ 
    [FieldOffset(8)] 
    public int right; 

    [FieldOffset(4)] 
    public int top; 

    [FieldOffset(0)] 
    public int left; 

    [FieldOffset(12)] 
    public int bottom; 
} 

Wartość FieldOffsetAttribute wskazuje położenie bajtu struktury z początku zmiennej.

0

Czy mam rację, zakładając, że mogę zmodyfikować strukturę zarządzaną pod względem specyfikatora dostępu dla każdej zmiennej, a nawet nazwy zmiennej, ale nie mogę zmienić kolejności zmiennych?

Tak, zgadza się. Ani specyfikator dostępu, ani nazwa zmiennej nie mają żadnego wpływu na sposób, w jaki struktura jest ułożona.