2012-01-02 12 views
21

voglio fare questo: (EDIT: il codice di esempio male, ignorare e passare sotto)C# array all'interno di una struttura

struct RECORD { 
    char[] name = new char[16]; 
    int dt1; 
} 
struct BLOCK { 
    char[] version = new char[4]; 
    int field1; 
    int field2; 
    RECORD[] records = new RECORD[15]; 
    char[] filler1 = new char[24]; 
} 

Ma non essendo in grado di dichiarare la dimensione dell'array nella struct, come faccio a riconfigurare questo?

MODIFICA: Il motivo del layout è che sto utilizzando BinaryReader per leggere un file scritto con le strutture C. Usando BinaryReader e un C# struct union (FieldOffset (0)), voglio caricare l'intestazione come una matrice di byte, quindi leggerla come originariamente intesa.

[StructLayout(LayoutKind.Sequential)] 
unsafe struct headerLayout 
{ 
    [FieldOffset(0)] 
    char[] version = new char[4]; 
    int fileOsn; 
    int fileDsn; 
    // and other fields, some with arrays of simple types 
} 

[StructLayout(LayoutKind.Explicit)] 
struct headerUnion     // 2048 bytes in header 
{ 
    [FieldOffset(0)] 
    public byte[] headerBytes;  // for BinaryReader 
    [FieldOffset(0)] 
    public headerLayout header;  // for field recognition 
} 
+10

fissi Buffer Dimensione - http://msdn.microsoft.it/it/us/library/zycewsya.aspx – Joren

+0

@ Joren, perché non aggiungerlo come risposta? – atoMerz

+0

Sapete che un C# 'char' è 2 byte mentre un C' char' di solito è 1 byte, giusto? – CodesInChaos

risposta

6

Non userei quel modello in primo luogo. Questo tipo di mappatura della memoria può essere appropriato in c, ma non in un linguaggio di alto livello come C#.

Scrivo semplicemente una chiamata al lettore binario per ogni membro che voglio leggere. Ciò significa che puoi usare le classi e scriverle in modo pulito e di alto livello.

Si occupa anche dei problemi di endian. Mentre la mappatura della memoria si interrompe quando viene utilizzata su diversi sistemi endian.

questione connessa: Casting a byte array to a managed structure


Quindi il codice sarà simile al seguente (aggiungere modificatori di accesso, ecc):

class Record 
{ 
    char[] name; 
    int dt1; 
} 
class Block { 
    char[] version; 
    int field1; 
    int field2; 
    RECORD[] records; 
    char[] filler1; 
} 

class MyReader 
{ 
    BinaryReader Reader; 

    Block ReadBlock() 
    { 
     Block block=new Block(); 
     block.version=Reader.ReadChars(4); 
     block.field1=Reader.ReadInt32(); 
     block.field2=Reader.ReadInt32(); 
     block.records=new Record[15]; 
     for(int i=0;i<block.records.Length;i++) 
      block.records[i]=ReadRecord(); 
     block.filler1=Reader.ReadChars(24); 
     return block; 
    } 

    Record ReadRecord() 
    { 
     ... 
    } 

    public MyReader(BinaryReader reader) 
    { 
     Reader=reader; 
    } 
} 
+0

Sembrerebbe più semplice se non stessi leggendo un array di un numero sconosciuto di blocchi 2K contenenti un array di 22 strutture contenenti 18 campi. Anche con solo 1000 record, sono 18000 righe di codice per leggere ogni campo nei record. Forse ho frainteso il tuo significato. –

+0

Non avresti 18000 righe di codice per 1000 record. Avresti un ciclo che elabora i 1000 record con 18 code code code nel corpo del loop. –

+0

Hai 1-2 righe di codice per campo. Ma non hai bisogno di attributi di layout, quindi il codice non sarebbe molto più lungo. Ma sarebbe verificabile, sicuro, più portatile e molto più pulito. Quando leggi un array, usi ovviamente un loop. – CodesInChaos

2

uso un costruttore con Parametri:

public struct RECORD { 
    public RECORD(int initial) { 
     name = new char[16]; 
     dt1 = initial; 
    } 
    public char[] name; 
    public int dt1; 
} 

...

var record = new RECORD(5); 
+13

Non è possibile scrivere un costruttore senza parametri in una struttura in C#. –

+1

Vedo che ho scritto una brutta domanda, la aggiornerò tra qualche minuto. –

+1

Sembra che tu abbia scritto una buona domanda con 7 voti positivi e 2 preferiti; ma la mia risposta in origine ha avuto un errore di compilazione che ho risolto mentre Jon stava facendo il suo commento. Purtroppo era troppo tardi. – Joe

15

Uso fixed size buffers:

[StructLayout(LayoutKind.Explicit)] 
unsafe struct headerUnion     // 2048 bytes in header 
{ 
    [FieldOffset(0)] 
    public fixed byte headerBytes[2048];  
    [FieldOffset(0)] 
    public headerLayout header; 
} 

Alternativ si può semplicemente utilizzare lo struct e leggerlo con il seguente metodo di estensione:

private static T ReadStruct<T>(this BinaryReader reader) 
     where T : struct 
{ 
    Byte[] buffer = new Byte[Marshal.SizeOf(typeof(T))]; 
    reader.Read(buffer, 0, buffer.Length); 
    GCHandle handle = default(GCHandle); 
    try 
    { 
     handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
    } 
    finally 
    { 
     if (handle.IsAllocated) 
      handle.Free(); 
    } 
} 
+0

Il buffer di dimensioni fisse funziona per tipi semplici, ma uno dei miei blocchi legge contiene un array di 22 record di 92 byte ciascuno. Il record contiene 18 campi. "Il tipo di buffer di dimensione fissa deve essere uno dei seguenti: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float o double" Ma sono molto più vicino all'intera soluzione grazie a fixed. –

+0

Felix, ma se ho una struct all'interno di struct, come posso ottenere la struct interna se uso il tuo 'ReadStruct' generico? – Konstantin

+1

@KonstantinK Se è solo una semplice struttura in un'altra struttura, il codice sopra dovrebbe funzionare. 'Marshal.SizeOf (typeof (T))' calcola la dimensione della struttura completa, incluse tutte le strutture all'interno. –

5

Array all'interno di strutture strutture non gestiti possono contenere array embedded. Per impostazione predefinita, questi campi di matrice incorporati vengono sottoposti a marshalling come SAFEARRAY. Nell'esempio seguente, s1 è un array incorporato che viene allocato direttamente all'interno della struttura stessa.

Unmanaged representation 
struct MyStruct { 
    short s1[128]; 
} 

array possono essere marshalling come UnmanagedType.ByValArray, che richiede di impostare il campo MarshalAsAttribute.SizeConst. La dimensione può essere impostata solo come costante. Il codice seguente mostra la corrispondente definizione gestita di MyStruct. C# VB

[StructLayout(LayoutKind.Sequential)] 
public struct MyStruct { 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1; 
} 
Problemi correlati