【问题标题】:Mapping Stream data to data structures in C#将 Stream 数据映射到 C# 中的数据结构
【发布时间】:2010-09-05 08:38:33
【问题描述】:

有没有办法将在流或数组上收集的数据映射到数据结构,反之亦然? 在 C++ 中,这只是将指向流的指针转换为我想要使用的数据类型的问题(反之亦然) 例如:在 C++ 中

Mystruct * pMyStrct = (Mystruct*)&SomeDataStream;
pMyStrct->Item1 = 25;

int iReadData = pMyStrct->Item2;

显然 C++ 方式是相当不安全的,除非您在读取传入数据时确定流数据的质量,但对于传出数据来说非常快速和容易。

【问题讨论】:

    标签: c# c++ data-structures


    【解决方案1】:

    大多数人使用.NET序列化(有更快的二进制和更慢的XML格式化程序,它们都依赖于反射并且在一定程度上具有版本容错性)

    但是,如果您想要最快(不安全)的方式 - 为什么不呢:

    写作:

    YourStruct o = new YourStruct();
    byte[] buffer = new byte[Marshal.SizeOf(typeof(YourStruct))];
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false);
    handle.Free();
    

    阅读:

    handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    o = (YourStruct)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(YourStruct));
    handle.Free();
    

    【讨论】:

      【解决方案2】:

      如果 lubos hasko 的回答不够安全,还有 really 不安全的方法,使用 C#中的指针。以下是我遇到的一些提示和陷阱:

      using System;
      using System.Runtime.InteropServices;
      using System.IO;
      using System.Diagnostics;
      
      // Use LayoutKind.Sequential to prevent the CLR from reordering your fields.
      [StructLayout(LayoutKind.Sequential)]
      unsafe struct MeshDesc
      {
          public byte NameLen;
          // Here fixed means store the array by value, like in C,
          // though C# exposes access to Name as a char*.
          // fixed also requires 'unsafe' on the struct definition.
          public fixed char Name[16];
          // You can include other structs like in C as well.
          public Matrix Transform;
          public uint VertexCount;
          // But not both, you can't store an array of structs.
          //public fixed Vector Vertices[512];
      }
      
      [StructLayout(LayoutKind.Sequential)]
      unsafe struct Matrix
      {
          public fixed float M[16];
      }
      
      // This is how you do unions
      [StructLayout(LayoutKind.Explicit)]
      unsafe struct Vector
      {
          [FieldOffset(0)]
          public fixed float Items[16];
          [FieldOffset(0)]
          public float X;
          [FieldOffset(4)]
          public float Y;
          [FieldOffset(8)]
          public float Z;
      }
      
      class Program
      {
          unsafe static void Main(string[] args)
          {
              var mesh = new MeshDesc();
              var buffer = new byte[Marshal.SizeOf(mesh)];
      
              // Set where NameLen will be read from.
              buffer[0] = 12;
              // Use Buffer.BlockCopy to raw copy data across arrays of primitives.
              // Note we copy to offset 2 here: char's have alignment of 2, so there is
              // a padding byte after NameLen: just like in C.
              Buffer.BlockCopy("Hello!".ToCharArray(), 0, buffer, 2, 12);
      
              // Copy data to struct
              Read(buffer, out mesh);
      
              // Print the Name we wrote above:
              var name = new char[mesh.NameLen];
              // Use Marsal.Copy to copy between arrays and pointers to arrays.
              unsafe { Marshal.Copy((IntPtr)mesh.Name, name, 0, mesh.NameLen); }
              // Note you can also use the String.String(char*) overloads
              Console.WriteLine("Name: " + new string(name));
      
              // If Erik Myers likes it...
              mesh.VertexCount = 4711;
      
              // Copy data from struct:
              // MeshDesc is a struct, and is on the stack, so it's
              // memory is effectively pinned by the stack pointer.
              // This means '&' is sufficient to get a pointer.
              Write(&mesh, buffer);
      
              // Watch for alignment again, and note you have endianess to worry about...
              int vc = buffer[100] | (buffer[101] << 8) | (buffer[102] << 16) | (buffer[103] << 24);
              Console.WriteLine("VertexCount = " + vc);
          }
      
          unsafe static void Write(MeshDesc* pMesh, byte[] buffer)
          {
              // But byte[] is on the heap, and therefore needs
              // to be flagged as pinned so the GC won't try to move it
              // from under you - this can be done most efficiently with
              // 'fixed', but can also be done with GCHandleType.Pinned.
              fixed (byte* pBuffer = buffer)
                  *(MeshDesc*)pBuffer = *pMesh;
          }
      
          unsafe static void Read(byte[] buffer, out MeshDesc mesh)
          {
              fixed (byte* pBuffer = buffer)
                  mesh = *(MeshDesc*)pBuffer;
          }
      }
      

      【讨论】:

        【解决方案3】:

        如果两边都是.net:

        认为您应该使用二进制序列化并发送 byte[] 结果。

        相信你的结构是完全可blittable的可能会很麻烦。

        您将支付一些开销(CPU 和网络),但会很安全。

        【讨论】:

          【解决方案4】:

          如果您需要手动填充每个成员变量,您可以通过使用 FormatterServices 按顺序检索与对象关联的变量类型列表,就原语而言对其进行概括。我不得不在一个项目中这样做,我有很多不同的消息类型来自流,我绝对不想为每条消息编写序列化器/反序列化器。

          这是我用来概括从 byte[] 反序列化的代码。

          public virtual bool SetMessageBytes(byte[] message)
              {
                  MemberInfo[] members = FormatterServices.GetSerializableMembers(this.GetType());
                  object[] values = FormatterServices.GetObjectData(this, members);
                  int j = 0;
          
                  for (int i = 0; i < members.Length; i++)
                  {
                      string[] var = members[i].ToString().Split(new char[] { ' ' });
                      switch (var[0])
                      {
                          case "UInt32":
                              values[i] = (UInt32)((message[j] << 24) + (message[j + 1] << 16) + (message[j + 2] << 8) + message[j + 3]);
                              j += 4;
                              break;
                          case "UInt16":
                              values[i] = (UInt16)((message[j] << 8) + message[j + 1]);
                              j += 2;
                              break;
                          case "Byte":
                              values[i] = (byte)message[j++];
                              break;
                          case "UInt32[]":
                              if (values[i] != null)
                              {
                                  int len = ((UInt32[])values[i]).Length;
                                  byte[] b = new byte[len * 4];
                                  Array.Copy(message, j, b, 0, len * 4);
                                  Array.Copy(Utilities.ByteArrayToUInt32Array(b), (UInt32[])values[i], len);
                                  j += len * 4;
                              }
                              break;
                          case "Byte[]":
                              if (values[i] != null)
                              {
                                  int len = ((byte[])values[i]).Length;
                                  Array.Copy(message, j, (byte[])(values[i]), 0, len);
                                  j += len;
                              }
                              break;
                          default:
                              throw new Exception("ByteExtractable::SetMessageBytes Unsupported Type: " + var[1] + " is of type " +  var[0]);
                      }
                  }
                  FormatterServices.PopulateObjectMembers(this, members, values);
                  return true;
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-04-22
            • 1970-01-01
            • 1970-01-01
            • 2020-09-30
            • 2012-05-09
            • 1970-01-01
            • 2019-06-07
            相关资源
            最近更新 更多