繁体   English   中英

结构中的 C# 数组

[英]C# array within a 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];
}

但是无法在结构中声明数组大小,我该如何重新配置​​它?

编辑:布局的原因是我使用 BinaryReader 读取用 C 结构编写的文件。 使用 BinaryReader 和 C# struct union (FieldOffset(0)),我想将标头作为字节数组加载,然后按照最初的意图读取它。

[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
}

使用固定大小的缓冲区

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

或者,您可以只使用结构并使用以下扩展方法读取它:

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

非托管结构可以包含嵌入式数组。 默认情况下,这些嵌入的数组字段被编组为 SAFEARRAY。 在以下示例中,s1 是一个嵌入数组,直接在结构本身内分配。

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

数组可以作为 UnmanagedType.ByValArray 进行封送处理,这需要您设置 MarshalAsAttribute.SizeConst 字段。 大小只能设置为常数。 以下代码显示了 MyStruct 的相应托管定义。 C#VB

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

我一开始不会使用这种模式。 这种内存映射可能适用于 c,但不适用于像 C# 这样的高级语言。

我只是为我想阅读的每个成员写一个对二进制阅读器的调用。 这意味着您可以使用类并以干净的高级方式编写它们。

它还处理字节序问题。 而内存映射在不同的字节序系统上使用时会中断。

相关问题: 将字节数组转换为托管结构


因此,您的代码将类似于以下内容(添加访问修饰符等):

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

使用不安全的代码和固定大小的缓冲区可以做到这一点: http : //msdn.microsoft.com/en-us/library/zycewsya.aspx

固定大小的缓冲区是结构的内联字节。 它们不像您的 char[] 那样位于单独的数组中。

除非你真的需要一个结构,否则你可以用一个类来做到这一点。 类基本上是一个结构体,使用方式完全相同,但它可以在内部包含方法。 这些方法之一是构造函数,一旦您使用“new”创建新实例,它将在其中初始化默认值。 要创建构造函数,请在其中放置一个与类同名的方法。 如果您愿意,它可以接收参数。

class RECORD 
{  
public int dt1;
public char[] name; 
public RECORD => name = new char[16] // if it is one-line the {} can be =>
}

class BLOCK 
    {
    public char[] version;
    public int  field1;
    public int  field2;
    public RECORD[] records;
    public char[] filler1;
    public BLOCK() 
        {
        records = new RECORD[15];
        filler1 = new char[24];
        version = new char[4];
        }      
    }

这样当你创建一个 BLOCK 类型的新项目时,它将被预先初始化:

var myblock = new BLOCK(); 
Console.WriteLine(myblock.records.Length); // returns 15
Console.WriteLine(myblock.records[0].Length); // returns 16
Console.WriteLine(myblock.filler1.Length); // returns 24

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM