如何在 C# 中将数组放入结构体中?

问题描述 投票:0回答:4

C++代码:

struct tPacket
{
    WORD word1;
    WORD word2;
    BYTE byte1;
    BYTE byte2;
    BYTE array123[8];
}

static char data[8192] = {0};
...
some code to fill up the array
...
tPacket * packet = (tPacket *)data;

我们不能在 C# 中那么简单地做到这一点。

请注意C++结构中有一个数组。

或者,使用这个源文件可以为我们完成这项工作,但如果结构中有一个数组则不行。

c# c++ pointers code-translation
4个回答
9
投票

我不确定你到底在问什么。您是否正在尝试在 C# 中获取等效的结构定义以用于普通的旧 C# 用法或用于互操作 (P/Invoke) 目的?如果是用于 P/Invoke,则以下结构将起作用

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tPacket {
    
    /// WORD->unsigned short
    public ushort word1;
    
    /// WORD->unsigned short
    public ushort word2;
    
    /// BYTE->unsigned char
    public byte byte1;
    
    /// BYTE->unsigned char
    public byte byte2;
    
    /// BYTE[8]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=8, ArraySubType=System.Runtime.InteropServices.UnmanagedType.I1)]
    public byte[] array123;
}

如果您正在寻找具有相同特征的普通旧式 C# 结构,则遗憾的是无法使用结构来实现。您不能在 C# 结构中定义恒定大小的内联数组,也不能通过初始值设定项强制该数组为特定大小。

托管世界中有两种替代选择。

使用具有填充数组的创建方法的结构

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tPacket {
    public ushort word1;
    public ushort word2;
    public byte byte1;
    public byte byte2;
    public byte[] array123;
    public static tPacket Create() { 
      return new tPacket() { array123 = new byte[8] };
    }
}

或者使用可以直接初始化 array123 成员变量的类。

编辑 OP watns 了解如何将 byte[] 转换为 tPacket 值

不幸的是,在 C# 中没有很好的方法来做到这一点。 C++ 非常适合此类任务,因为它的类型系统非常弱,您可以选择将字节流视为特定结构(邪恶的指针转换)。

这在 C# 不安全代码中可能是可能的,但我不相信它是这样。

本质上,您要做的就是手动解析字节并将它们分配给结构中的各个值。或者编写一个本机方法,将 C 风格转换和 PInvoke 转换为该函数。


2
投票

您正在寻找的内容(如果您使用类似@JaredPar 发布的类似结构定义)是这样的:

tPacket t = new tPacket();
byte[] buffer = new byte[Marshal.SizeOf(typeof(tPacket))];
socket.Receive(buffer, 0, buffer.length, 0);

GCHandle pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
t = (tPacket)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(tPacket));
pin.free();

//do stuff with your new tPacket t

1
投票

它也可以使用不安全的代码来完成,尽管它限制了程序可以运行的上下文,并且自然地引入了安全缺陷的可能性。 优点是您可以使用指针直接从数组转换为结构,并且如果您只想在结构中添加或删除字段,那么它也是免维护的。但是,访问数组需要使用固定语句,因为当结构体包含在对象中时,GC 仍然可以在内存中移动该结构体。

这是我用来解释 UDP 数据包的不安全结构的一些修改代码:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public unsafe struct UnsafePacket
{
    int time;
    short id0;
    fixed float acc[3];
    short id1;
    fixed float mat[9];

    public UnsafePacket(byte[] rawData)
    {
        if (rawData == null)
            throw new ArgumentNullException("rawData");
        if (sizeof(byte) * rawData.Length != sizeof(UnsafePacket))
            throw new ArgumentException("rawData");

        fixed (byte* ptr = &rawData[0])
        {
            this = *(UnsafePacket*)rawPtr;
        }
    }

    public float GetAcc(int index)
    {
        if (index < 0 || index >= 3)
            throw new ArgumentOutOfRangeException("index");
        fixed (UnsafePacket* ptr = &acc)
        {
            return ptr[index];
        }
    }

    public float GetMat(int index)
    {
        if (index < 0 || index >= 9)
            throw new ArgumentOutOfRangeException("index");
        fixed (UnsafePacket* ptr = &mat)
        {
            return ptr[index];
        }
    }

            // etc. for other properties
}

对于这种类型的代码,检查数组的长度是否与结构的大小完全匹配非常重要,否则您将面临一些令人讨厌的缓冲区溢出。由于 unsafe 关键字已应用于整个结构体,因此您无需将每个方法或代码块标记为单独的 unsafe 语句。


1
投票

您可以通过在结构中编写用于访问的函数,将外界看起来像固定大小的数组的内容放置在安全结构中。例如,这是一个安全结构内的固定 4 x 4 双精度数组:

public struct matrix4 //  4 by 4 matrix  
{  
    //  
    //  Here we will create a square matrix that can be written to and read from similar  
    //  (but not identical to) using an array.  Reading and writing into this structure  
    //  is slower than using an array (due to nested switch blocks, where nest depth  
    //  is the dimensionality of the array, or 2 in this case).  A big advantage of this  
    //  structure is that it operates within a safe context.  
    //  
    private double a00; private double a01; private double a02; private double a03;  
    private double a10; private double a11; private double a12; private double a13;  
    private double a20; private double a21; private double a22; private double a23;  
    private double a30; private double a31; private double a32; private double a33;  
    //
    public void AssignAllZeros()                    //  Zero out the square matrix  
    { /* code */}               
    public double Determinant()                     //  Common linear algebra function  
    { /* code */}  
    public double Maximum()                         //  Returns maximum value in matrix  
    { /* code */}  
    public double Minimum()                         //  Minimum value in matrix  
    { /* code */}  
    public double Read(short row, short col)        //  Outside read access   
    { /* code */}  
    public double Read(int row, int col)            //  Outside read access overload  
    { /* code */}  
    public double Sum()                             //  Sum of 16 double precision values  
    {  
        return a00 + a01 + a02 + a03 + a10 + a11 + a12 + a13 + a20 + a21 + a22 + a23 + a30 + a31 + a32 + a33;  
    }  
    public void Write(short row, short col, double doubleValue)  //  Write access to matrix  
    { /* code */}  
    public void Write(int row, int col, double doubleValue)      //  Write access overload  
    { /* code */}              
}
© www.soinside.com 2019 - 2024. All rights reserved.