2008-08-05 11 views
19

Existe-t-il un moyen de mapper des données collectées sur un flux ou un tableau vers une structure de données ou vice-versa? En C++ ce serait tout simplement une question de jeter un pointeur sur le flux en tant que type de données que je veux utiliser (ou vice-versa pour l'inverse) par exemple: voie en C++Mappage de données de flux vers des structures de données en C#

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

int iReadData = pMyStrct->Item2; 

évidemment le C++ est assez dangereux, sauf si vous êtes sûr de la qualité des données de flux lors de la lecture des données entrantes, mais pour les données sortantes est super rapide et facile.

Répondre

16

La plupart des gens utilisent la sérialisation .NET (il est plus rapide binaire et formatter XML plus lent, ils dépendent tous deux de réflexion et sont la version tolérante à certains degrés)

Cependant, si vous voulez le plus rapide (dangereux) façon - pourquoi pas:

Rédaction:

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

lecture:

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

si son .net sur les deux côtés:

pense que vous devriez utiliser sérialisation binaire et envoyer l'octet résultat []. Faire confiance à votre structure pour être entièrement blittable peut être un problème.

vous paierez dans certains frais généraux (à la fois cpu et réseau) mais sera sûr.

2

Si vous devez remplir manuellement chaque variable membre, vous pouvez la généraliser un peu en ce qui concerne les primitives en utilisant FormatterServices pour récupérer dans l'ordre la liste des types de variables associés à un objet. J'ai dû faire ceci dans un projet où j'avais beaucoup de différents types de message venant du courant et je ne voulais définitivement pas écrire le sérialiseur/désérialiseur pour chaque message.

Voici le code que j'ai utilisé pour généraliser la désérialisation à partir d'un octet [].

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

Dans le cas où la réponse de lubos hasko n'était pas assez dangereuse, il y a aussi vraiment façon dangereuse, en utilisant pointeurs en C#. Voici quelques conseils et pièges que j'ai rencontrés:

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