【问题标题】:How can I put an array inside a struct in C#?如何在 C# 中将数组放入结构中?
【发布时间】:2010-10-15 23:29:16
【问题描述】:

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++ 结构中有一个数组。

或者,使用this source file 可以为我们完成这项工作,但如果结构中存在数组则不行。

【问题讨论】:

    标签: c# c++ pointers code-translation


    【解决方案1】:

    我不确定你到底在问什么。您是否尝试在 C# 中为普通的旧 C# 用法或互操作(PInvoke)目的获得等效的结构定义?如果是用于 PInvoke,follownig 结构将起作用

    [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 想知道如何将 byte[] 转换为 tPacket 值

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

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

    基本上你要做的就是手动解析字节并将它们分配给结构中的各种值。或者编写一个本地方法,将 C 样式转换和 PInvoke 放入该函数中。

    【讨论】:

    • @John,两个样本都包含结构中的数组。我不太确定你在问什么
    • @JaredPar,C++代码与接收包一起使用。当接收到包时,变量包将数据存储在结构tPacket中,稍后访问它们。真正的成员是Size,Opcode ,SeedCRC,SeedSecurity,EncryptedData 和致命的 - 河豚,这是数组。
    • 我在问如何在 C# 中做到这一点?将数据放入结构中,就像在 C++ 中一样。请检查上面的代码。
    • 很好的答案。帮助我解决了将带有数组成员的结构从 c# 传递到 c++ 的问题。谢谢!
    【解决方案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
    

    【讨论】:

    • 另外,您不必将此代码放在不安全的块中。
    • scotty2012,谢谢。我在字节顺序上有一些问题,但我会尝试自己解决。如果没有,那么我会发布另一个问题。谢谢两位!
    • 有时你必须弄乱结构布局属性中的 Pack 设置。我认为 c# 中的默认值是 8 个字节,但 c 是 4,或者类似的东西。我一时想不起来了。
    【解决方案3】:

    它也可以使用不安全的代码来完成,尽管它限制了程序可以运行的上下文,并且自然会引入安全漏洞的可能性。 优点是您可以使用指针直接从数组转换为结构,并且如果您只想从结构中添加或删除字段,它也是免维护的。但是,访问数组需要使用固定语句,因为当结构包含在对象中时,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 语句。

    【讨论】:

      【解决方案4】:

      您可以通过在结构中编写函数以供访问,从而将外部世界看起来像是固定大小的数组放置在安全结构中。例如,这是一个安全结构中的固定 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 */}              
      }
      

      【讨论】:

      • 避免在属性设置器以外的实例方法中修改结构的字段。要么公开this[int,int] 属性,要么公开将Matrix4 作为ref 参数的静态方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      相关资源
      最近更新 更多