2012-01-02 66 views
21

Muốn làm được điều này: (EDIT: mẫu mã xấu, bỏ qua và bỏ qua bên dưới)C# mảng trong một struct

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

Nhưng không có khả năng tuyên bố kích thước mảng trong struct, làm thế nào để cấu hình lại này?

EDIT: Lý do bố cục là tôi đang sử dụng BinaryReader để đọc tệp được viết bằng cấu trúc C. Sử dụng BinaryReader, và một C# struct union (FieldOffset (0)), tôi muốn tải tiêu đề như là một mảng byte, sau đó đọc nó như nó đã được dự định ban đầu.

[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

Bộ đệm kích thước cố định - http://msdn.microsoft.com/en-us/library/zycewsya.aspx – Joren

+0

@Joren, tại sao không thêm nó làm câu trả lời? – atoMerz

+0

Bạn biết rằng C# 'char' là 2 byte trong khi C' char' thường là 1 byte, phải không? – CodesInChaos

Trả lời

6

tôi sẽ không sử dụng mô hình mà ở nơi đầu tiên. Kiểu ánh xạ bộ nhớ này có thể thích hợp trong c, nhưng không phải trong một ngôn ngữ cấp cao như C#.

Tôi chỉ viết một cuộc gọi tới trình đọc nhị phân cho từng thành viên mà tôi muốn đọc. Điều này có nghĩa là bạn có thể sử dụng các lớp và viết chúng theo một cách mức độ cao.

Nó cũng đảm nhiệm các vấn đề về cuối cùng. Trong khi đó việc ánh xạ bộ nhớ sẽ bị phá vỡ khi được sử dụng trên các hệ thống cuối khác nhau.

câu hỏi liên quan: Casting a byte array to a managed structure


Vì vậy, mã của bạn sẽ trông tương tự như sau (thêm từ bổ nghĩa truy cập vv):

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

Điều đó có vẻ đơn giản hơn nếu tôi không đọc một mảng số lượng không xác định gồm 2K khối chứa một mảng gồm 22 cấu trúc chứa 18 trường. Ngay cả với chỉ 1000 hồ sơ, đó là 18.000 dòng mã để đọc từng lĩnh vực trong hồ sơ. Có lẽ tôi hiểu lầm ý nghĩa của bạn. –

+0

Bạn sẽ không có 18000 dòng mã cho 1000 bản ghi. Bạn sẽ có một vòng lặp xử lý 1000 bản ghi với 18 dòng mã trong phần thân của vòng lặp. –

+0

Bạn có 1-2 dòng mã trên mỗi trường. Nhưng bạn không cần thuộc tính bố cục, vì vậy mã sẽ không còn dài hơn nữa. Nhưng nó sẽ được kiểm chứng, an toàn, di động hơn và sạch hơn nhiều. Khi đọc một mảng bạn rõ ràng sử dụng một vòng lặp. – CodesInChaos

2

sử dụng một constructor với thông số:

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

Bạn không thể viết một hàm tạo tham số trong cấu trúc trong C#. –

+1

Tôi thấy rằng tôi đã viết một câu hỏi xấu, sẽ cập nhật nó trong vài phút nữa. –

+1

Có vẻ như bạn đã viết một câu hỏi hay với 7 phiếu bầu và 2 mục yêu thích; nhưng câu trả lời của tôi ban đầu đã có một lỗi thời gian biên dịch mà tôi cố định trong khi Jon đã đưa ra nhận xét của mình. Than ôi đã quá trễ. – Joe

2

Sử dụng mã không an toàn và đệm kích thước cố định này có thể được thực hiện: http://msdn.microsoft.com/en-us/library/zycewsya.aspx

Bộ đệm kích thước cố định là các byte nội tuyến của cấu trúc. Họ không sống bên trong một mảng riêng biệt như char [] của bạn.

15

Sử dụng 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 bạn chỉ có thể sử dụng các cấu trúc và đọc nó với phương pháp mở rộng sau:

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

Bộ đệm có kích thước cố định đang hoạt động đối với các loại đơn giản, nhưng một trong các khối đọc của tôi chứa một mảng gồm 22 bản ghi có kích thước 92 byte. Bản ghi có 18 trường. "Loại đệm kích thước cố định phải là một trong các loại sau: bool, byte, ngắn, int, dài, char, sbyte, ushort, uint, ulong, float hoặc double" Nhưng tôi gần gũi hơn với toàn bộ giải pháp nhờ cố định. –

+0

Felix, nhưng nếu tôi có cấu trúc bên trong struct, làm thế nào tôi có thể nhận được bên trong struct nếu tôi sử dụng chung 'ReadStruct'? – Konstantin

+1

@KonstantinK Nếu nó chỉ là một cấu trúc đơn giản trong cấu trúc khác thì mã ở trên sẽ hoạt động. 'Marshal.SizeOf (typeof (T))' tính toán kích thước của cấu trúc hoàn chỉnh, bao gồm tất cả các cấu trúc bên trong. –

5

Mảng trong cấu trúc cấu trúc Switch có thể chứa mảng nhúng. Theo mặc định, các trường mảng được nhúng này được sắp xếp như một SAFEARRAY. Trong ví dụ sau, s1 là một mảng nhúng được phân bổ trực tiếp trong chính cấu trúc đó.

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

Mảng có thể được marshaled như UnmanagedType.ByValArray, mà đòi hỏi bạn phải thiết lập các lĩnh vực MarshalAsAttribute.SizeConst. Kích thước có thể được thiết lập chỉ là một hằng số. Đoạn mã sau cho thấy định nghĩa được quản lý tương ứng của MyStruct. C# VB

[StructLayout(LayoutKind.Sequential)] 
public struct MyStruct { 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1; 
} 
Các vấn đề liên quan