2008-08-05 4 views
19

Есть ли способ сопоставления данных, собранных по потоку или массиву, с структурой данных или наоборот? В C++ это будет просто вопрос приведения указателя на поток как тип данных я хочу использовать (или наоборот для реверса) например: в C++Сопоставление данных потока с данными в C#

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

int iReadData = pMyStrct->Item2; 

, очевидно, путь C++ является довольно небезопасно, если вы не уверены в качестве данных потока при чтении входящих данных, но для исходящих данных очень быстро и просто.

ответ

16

Большинство людей используют .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(); 

Reading:

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

, если его .net с обеих сторон:

думаю, вы должны использовать бинарную сериализацию и отправить байт [] результат.

Доверяя, что ваша структура будет полностью гореть, может возникнуть проблема.

Вы оплачиваете некоторые издержки (как процессор, так и сеть), но будете в безопасности.

2

Если вам нужно заполнить каждую переменную-член вручную, вы можете обобщить ее немного до примитивов, используя FormatterServices для извлечения в порядке списка типов переменных, связанных с объектом. Я должен был сделать это в проекте, где у меня было много разных типов сообщений, выходящих из потока, и я определенно не хотел писать сериализатор/десериализатор для каждого сообщения.

Вот код, который я использовал для обобщения десериализации из байта [].

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

В случае, если ответ lubos hasko был небезопасным, есть и действительно небезопасный способ, используя указатели на 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; 
    } 
} 
Смежные вопросы