2012-01-02 33 views
21

desee hacer esto: (EDIT: código de ejemplo malo, ignorar y pasar a continuación)C# matriz dentro de una estructura

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

Pero al no poder declarar tamaños de matriz de estructura, ¿cómo vuelvo a configurar esto?

EDITAR: El motivo del diseño es que estoy usando BinaryReader para leer un archivo escrito con estructuras C. Utilizando BinaryReader y una unión estructural C# (FieldOffset (0)), quiero cargar el encabezado como una matriz de bytes, y luego leerlo como estaba previsto originalmente.

[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

Tamaño fijo Buffers - http://msdn.microsoft.com/en-us/library/zycewsya.aspx – Joren

+0

@Joren, ¿por qué no agregarlo como respuesta? – atoMerz

+0

Usted sabe que un C# 'char' es de 2 bytes, mientras que un C' char' es generalmente de 1 byte, ¿no? – CodesInChaos

Respuesta

6

yo no usaría ese patrón en el primer lugar. Este tipo de mapeo de memoria puede ser apropiado en c, pero no en un lenguaje de alto nivel como C#.

Me gustaría escribir una llamada al lector binario para cada miembro que quiero leer. Esto significa que puede usar clases y escribirlas de una manera limpia y de alto nivel.

También se ocupa de los problemas endian. Mientras que la asignación de memoria se romperá cuando se use en diferentes sistemas endian.

pregunta relacionada: Casting a byte array to a managed structure


Así que su código será similar a la siguiente (añadir modificadores de acceso, etc.):

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

Eso parecería más simple si no estuviera leyendo una matriz de un número desconocido de bloques 2K que contienen una matriz de 22 estructuras que contienen 18 campos. Incluso con solo 1000 registros, eso equivale a 18000 líneas de código para leer cada campo en los registros. Tal vez malinterpreté tu significado. –

+0

No tendría 18000 líneas de código para 1000 registros. Tendría un bucle que procesa los 1000 registros con código de código de 18 líneas en el cuerpo del bucle. –

+0

Tiene 1-2 líneas de código por campo. Pero no necesita atributos de diseño, por lo que el código no sería mucho más largo. Pero sería verificable, seguro, más portátil y mucho más limpio. Cuando lee una matriz, obviamente usa un bucle. – CodesInChaos

2

uso un constructor con parámetros :

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

No puede escribir un constructor sin parámetros en una estructura en C#. –

+1

Veo que escribí una pregunta incorrecta, la actualizaré en unos minutos. –

+1

Parece que usted escribió una buena pregunta con 7 votos y 2 favoritos; pero mi respuesta originalmente tenía un error de tiempo de compilación que arreglé mientras Jon hacía su comentario. Por desgracia, fue demasiado tarde. – 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 que sólo puede utilizar la estructura y leerlo con el siguiente método de extensión:

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

El búfer de tamaño fijo funciona para tipos simples, pero una de mis lecturas de bloque contiene una matriz de 22 registros de 92 bytes cada uno. El registro contiene 18 campos. "El tipo de buffer de tamaño fijo debe ser uno de los siguientes: bool, byte, corto, int, largo, char, sbyte, ushort, uint, ulong, float o double" Pero estoy mucho más cerca de la solución completa gracias a lo fijo. –

+0

Félix, pero si tengo struct dentro de struct, ¿cómo puedo obtener struct interna si uso su 'ReadStruct 'genérico? – Konstantin

+1

@KonstantinK Si es simplemente una estructura simple en otra estructura, el código anterior debería funcionar. 'Marshal.SizeOf (typeof (T))' calcula el tamaño de la estructura completa, incluidas todas las estructuras internas. –

5

matrices dentro de las estructuras estructuras no administradas pueden contener matrices embebidos. Por defecto, estos campos de matriz incrustados se clasifican como SAFEARRAY. En el siguiente ejemplo, s1 es una matriz incrustada que se asigna directamente dentro de la propia estructura.

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

Las matrices pueden ser calculan como UnmanagedType.ByValArray, lo que requiere que se configure el campo MarshalAsAttribute.SizeConst. El tamaño se puede establecer solo como una constante. El siguiente código muestra la definición administrada correspondiente de MyStruct. C# VB

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