2008-08-05 5 views
19

¿Hay alguna forma de correlacionar los datos recopilados en una secuencia o matriz con una estructura de datos o viceversa? En C++ esto podría ser simplemente una cuestión de echar un puntero a la corriente como un tipo de datos que desea utilizar (o viceversa para el reverso) por ejemplo: en C++Asignación de datos de secuencia a estructuras de datos en C#

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

int iReadData = pMyStrct->Item2; 

obviamente la manera de C++ se bastante inseguro a menos que esté seguro de la calidad de los datos de la secuencia al leer los datos entrantes, pero para los datos salientes es muy rápido y fácil.

Respuesta

16

La mayoría de la gente utiliza .NET serialización (hay binario más rápido y más lento formateador XML, ambos dependen de la reflexión y son tolerantes a la versión cierto grado)

Sin embargo, si desea que el más rápido (inseguro) manera - por qué no:

Escritura:

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

lectura:

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

si su .net en ambos lados:

piensan que debe utilizar la serialización binaria y enviar el [] resultado de bytes.

confiando en que su estructura sea completamente blittable puede ser un problema.

pagará algunos gastos generales (tanto la CPU como la red), pero estarán a salvo.

2

Si necesita rellenar cada variable miembro a mano, puede generalizarla un poco en cuanto a las primitivas mediante el uso de FormatterServices para recuperar en orden la lista de tipos de variables asociadas con un objeto. Tuve que hacer esto en un proyecto en el que tenía muchos tipos diferentes de mensajes saliendo de la transmisión y definitivamente no quería escribir el serializador/deserializador para cada mensaje.

Aquí está el código que solía generalizar la deserialización de un 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; 
    } 
5

En caso de que la respuesta de lubos hasko no fuera lo suficientemente segura, también existe la realmente manera insegura, usando punteros en C#. Aquí hay algunos consejos y trampas con los que me he encontrado:

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