2009-07-25 10 views
8

Próbuję zrozumieć, dlaczego drugi przykład poniżej działa bez problemów, ale pierwszy przykład daje mi wyjątek poniżej. Wydaje mi się, że oba przykłady powinny stanowić wyjątek w oparciu o opis. Czy ktoś może mnie oświecić?C# StructLayout.Explicit Question

Nieobsłużone Wyjątek System.TypeLoadException: Nie można typ obciążenia 'StructTest.OuterType' z zestawu 'StructTest, wersja = 1.0.0.0, culture = neutralne TokenKluczaPublicznego = null' ponieważ zawiera pole obiektu pod przesunięcie 0, które jest niepoprawnie wyrównane lub nałożone przez pole nie będące obiektem.
na StructTest.Program.Main (String [] args) Naciśnij dowolny klawisz, aby kontynuować. . .

Przykład 1

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace StructTest 
{ 
    [StructLayout(LayoutKind.Sequential, Pack = 1)] 
    struct InnerType 
    { 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] 
     char[] buffer; 
    } 

    [StructLayout(LayoutKind.Explicit)] 
    struct OuterType 
    { 
     [FieldOffset(0)] 
     int someValue; 

     [FieldOffset(0)] 
     InnerType someOtherValue; 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      OuterType t = new OuterType(); 
      System.Console.WriteLine(t); 
     } 
    } 
} 

Przykład 2

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace StructTest 
{ 
    [StructLayout(LayoutKind.Sequential, Pack = 1)] 
    struct InnerType 
    { 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] 
     char[] buffer; 
    } 

    [StructLayout(LayoutKind.Explicit)] 
    struct OuterType 
    { 
     [FieldOffset(4)] 
     private int someValue; 

     [FieldOffset(0)] 
     InnerType someOtherValue; 

    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      OuterType t = new OuterType(); 
      System.Console.WriteLine(t); 
     } 
    } 
} 

Odpowiedz

9

wspólne wykonawcze język zawiera weryfikatora zapewniający, że kod uruchomiony (zweryfikowania II) nie może ewentualnie uszkodzona pamięć w zarządzanym środowisku. Zapobiega to zadeklarowaniu takiej struktury, w której pola nakładają się. Zasadniczo twoja struktura zawiera dwa elementy danych. Jedna liczba całkowita (4 bajty) i natywna liczba całkowita (wielkość wskaźnika). W 32-bitowym środowisku CLR, w którym najprawdopodobniej działasz z kodem, char[] zajmie 4 bajty, więc jeśli wstawisz liczbę całkowitą mniejszą niż cztery bajty od początku struktury, będziesz mieć nakładające się pola. Warto zauważyć, że oba fragmenty kodu ulegają awarii w 64-bitowym środowisku wykonawczym, ponieważ rozmiar wskaźnika wynosi 8 bajtów.

+0

Rozumiem, więc jeśli chciałbym zrobić to poprawnie, aby działało poprawnie na maszynach 32- lub 64-bitowych, musiałbym użyć odpowiednio offsetu 8 i 0, prawda? Problem polega na tym, że to, co naprawdę chciałem stworzyć, było związkiem i wygląda na to, że to nie będzie jeden, jeśli nie będę mógł użyć tego samego przesunięcia. –

+0

Tak. Będzie działać na CLR x64 z przesunięciami 8 i 0. Zauważ, że podczas działania fragmentu kodu, jeśli robisz to w rzeczywistej aplikacji, prawdopodobnie łączysz się z pewnymi niezarządzanymi rzeczami, które wymagałyby dokładnych przesunięć. Jeśli ustawisz go na 8, może zawieść na komputerach 32-bitowych (nie ten sam fragment, ale niezarządzany kod, z którym się łączysz). –

+1

Przy okazji, możesz ** może ** mieć zachodzące na siebie pola, jeśli są * niezarządzanego typu *, ale tablica .NET jest typem odniesienia (którego nie można uznać za typ niezarządzany według specyfikacji C#). Więc nie możesz mieć typu referencyjnego jako członka unii w języku C#. Jeśli naprawdę tego potrzebujesz, powinieneś rozważyć użycie takich typów wskaźników jak elementy struktury zamiast tablicy. –

1

Doszedłem do wniosku, że odpowiem na rozwiązanie, którego użyłem do stworzenia związku - co było moją pierwotną intencją. Użyłem niebezpiecznej struktury i stałej tablicy, a następnie użyłem właściwości do interakcji ze stałą tablicą. Wierzę, że powinno to robić to, co chcę.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace StructTest 
{ 

    [StructLayout(LayoutKind.Explicit)] 
    unsafe struct OuterType 
    { 
     private const int BUFFER_SIZE = 100; 

     [FieldOffset(0)] 
     private int transactionType; 

     [FieldOffset(0)] 
     private fixed byte writeBuffer[BUFFER_SIZE]; 

     public int TransactionType 
     { 
      get { return transactionType; } 
      set { transactionType = value; } 
     } 

     public char[] WriteBuffer 
     { 
      set 
      { 
       char[] newBuffer = value; 

       fixed (byte* b = writeBuffer) 
       { 
        byte* bptr = b; 
        for (int i = 0; i < newBuffer.Length; i++) 
        { 
         *bptr++ = (byte) newBuffer[i]; 
        } 
       } 
      } 

      get 
      { 
       char[] newBuffer = new char[BUFFER_SIZE]; 

       fixed (byte* b = writeBuffer) 
       { 
        byte* bptr = b; 
        for (int i = 0; i < newBuffer.Length; i++) 
        { 
         newBuffer[i] = (char) *bptr++; 
        } 
       } 

       return newBuffer; 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      OuterType t = new OuterType(); 
      System.Console.WriteLine(t); 
     } 
    } 
}