2009-07-25 9 views
8

Sto cercando di capire perché il secondo esempio di seguito funziona senza problemi, ma il primo esempio mi dà l'eccezione qui sotto. Mi sembra che entrambi gli esempi dovrebbero dare un'eccezione basata sulla descrizione. Qualcuno può illuminarmi?C# StructLayout.Explicit Domanda

eccezione non gestita: System.TypeLoadException: Impossibile tipo di carico 'StructTest.OuterType' da assembly 'StructTest, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' perché contiene oggetto campo a offset 0 che è allineato in modo errato o sovrapposto da un campo non oggetto.
presso StructTest.Program.Main (String [] args) Premere un tasto qualsiasi per continuare. . .

Esempio 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); 
     } 
    } 
} 

Esempio 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); 
     } 
    } 
} 

risposta

9

Common Language Runtime contiene un verificatore che consente di verificare il codice in esecuzione (verificabile IL) non può, eventualmente, memoria corrotta nell'ambiente gestito. Ciò impedisce di dichiarare una tale struttura in cui i campi si sovrappongono. Fondamentalmente, la tua struct contiene due membri di dati. Un numero intero (che è 4 byte) e un intero nativo (dimensione del puntatore). Su un CLR a 32 bit, in cui probabilmente si sta eseguendo il codice, lo char[] impiegherà 4 byte, quindi se si inserisce il numero intero inferiore a quattro byte dall'inizio della struttura, si avranno campi sovrapposti. È interessante notare che entrambi i frammenti di codice con fail su un runtime a 64 bit, come la dimensione del puntatore è di 8 byte.

+0

Vedo, quindi, se volessi farlo correttamente in modo che funzionasse correttamente su macchine a 32 o 64 bit, avrei bisogno di usare un offset rispettivamente di 8 e 0, corretto? Il problema è che quello che volevo veramente creare era un sindacato e sembra che questo finirà per non essere uno se non posso usare lo stesso offset. –

+0

Sì. Funzionerà su CLR x64 con offset 8 e 0. Si noti che mentre lo snippet di codice funziona, se lo si fa in un'app per il mondo reale, si sta probabilmente interagendo con alcune cose non gestite, che si aspettano gli offset esatti. Se lo imposti su 8, potrebbe non funzionare su macchine a 32 bit (non questo frammento stesso, ma il codice non gestito con cui stai interagendo). –

+1

A proposito, ** puoi ** avere campi sovrapposti se sono di * tipi non gestiti *, ma un array .NET è un tipo di riferimento (che non può essere considerato un tipo non gestito per specifica C#). Quindi non puoi avere un tipo di riferimento come membro di un sindacato in C#. Se hai davvero bisogno di questo, dovresti prendere in considerazione l'uso di cose come i tipi di puntatore come membri struct invece di un array. –

1

Ho pensato di rispondere con la soluzione che ho usato per creare l'unione, che era la mia intenzione originale. Ho usato una struttura non sicura e un array fisso e poi ho usato una proprietà per interagire con l'array fisso. Credo che questo dovrebbe fare ciò che voglio.

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); 
     } 
    } 
}