结构中的C#数组

结构中的C#数组,c#,C#,要这样做: (编辑:错误的示例代码,忽略并跳过以下内容) 但由于无法在struct中声明数组大小,如何重新配置 编辑:布局的原因是我使用BinaryReader读取用C结构编写的文件。使用BinaryReader和C#struct union(FieldOffset(0)),我希望将头作为字节数组加载,然后按照最初的意图读取它 [StructLayout(LayoutKind.Sequential)] unsafe struct headerLayout { [FieldOffset(0

要这样做: (编辑:错误的示例代码,忽略并跳过以下内容)

但由于无法在struct中声明数组大小,如何重新配置

编辑:布局的原因是我使用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
}

使用不安全代码和固定大小的缓冲区可以做到:

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

使用:

Alternativ您可以使用结构并使用以下扩展方法读取它:

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();
    }
}
private static T ReadStruct(此二进制读取器)
其中T:struct
{
Byte[]buffer=新字节[Marshal.SizeOf(typeof(T))];
reader.Read(buffer,0,buffer.Length);
GCHandle=default(GCHandle);
尝试
{
handle=GCHandle.Alloc(缓冲区,GCHandleType.pinted);
返回(T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(T));
}
最后
{
如果(句柄.已分配)
handle.Free();
}
}

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

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

它还处理endian问题。然而,当在不同的endian系统上使用时,内存映射将中断

相关问题:


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

课堂记录
{
字符[]名称;
int dt1;
}
类块{
字符[]版本;
int field1;
int field2;
记录[]项记录;
字符[]填充1;
}
类MyReader
{
二进制读取器;
Block ReadBlock()
{
块=新块();
block.version=Reader.ReadChars(4);
block.field1=Reader.ReadInt32();
block.field2=Reader.ReadInt32();
block.records=新记录[15];

for(int i=0;i非托管结构可以包含嵌入式数组。默认情况下,这些嵌入式数组字段被封送为SAFEARRAY。在以下示例中,s1是直接在结构本身中分配的嵌入式数组

Unmanaged representation
struct MyStruct {
    short s1[128];
}
数组可以封送为UnmanagedType.ByValArray,这要求您设置MarshalAsAttribute.SizeConst字段。大小只能设置为常量。下面的代码显示了MyStruct的相应托管定义。 C#VB


除非你真的需要一个结构,否则你可以用一个类来实现这一点。一个类基本上是一个结构,使用的方式完全相同,但它可以包含内部的方法。其中一个方法是构造函数,一旦你用“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

固定大小的缓冲区-@Joren,为什么不把它作为一个答案来添加呢?你知道一个C#
char
是2字节,而一个C
char
通常是1字节,对吧?是的,我遇到了这个问题,在声明中添加了Pack=1。不过这是一个好主意!@RobertKerr我认为
Pack=1
不会解决这个问题。Pack会影响字段之间的填充,而不是大小固定大小的缓冲区适用于简单类型,但我的一个块读取包含22条记录的数组,每条记录92字节。该记录包含18个字段。“固定大小的缓冲区类型必须是以下类型之一:bool、byte、short、int、long、char、sbyte、ushort、uint、ulong、float或double”但是由于fixed.Felix,我更接近于整个解决方案,但是如果我在struct中有struct,那么如果我使用你的泛型
ReadStruct
,我如何获得内部struct呢?@KonstantinK如果它只是另一个结构中的一个普通结构,那么上面的代码应该可以工作。
Marshal.SizeOf(typeof(T))
计算完整结构的大小,包括内部的所有结构。谢谢你,Felix!我会尝试这样做!不幸的是,它不起作用…我创建了单独的主题。明天我将打开bounty…也许你可以建议任何解决方案…如果我没有阅读未知数量的2K块conta数组,这看起来会更简单一个包含18个字段的22个结构的数组。即使只有1000条记录,也需要18000行代码来读取记录中的每个字段。也许我误解了你的意思。1000条记录不会有18000行代码。你会有一个循环,用循环体中的18行代码来处理1000条记录。你有1-2行代码每个字段有5个代码。但是你不需要布局属性,所以代码不会太长。但它将是可验证的、安全的、更可移植的和更干净的。当读取数组时,你显然使用循环。我喜欢你的名字。这当然是我有时的感受。你的解决方案,比我尝试的更直接。我注意到使用
Marshal.SizeOf
这个结构确实包装了
SizeOf(short)*128个字节。但是这个内存在哪里?我正在尝试这样做,而不拉一个非托管数组。我只想使用在第二个示例中分配的内存,但是当我尝试访问它时,数组被声明为空引用。
Unmanaged representation
struct MyStruct {
    short s1[128];
}
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1;
}
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];
        }      
    }
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