2008-08-05 5 views
19

스트림 또는 배열에서 수집 된 데이터를 데이터 구조에 매핑하거나 그 반대의 방식으로 매핑하는 방법이 있습니까? C++에서 분명히C#의 데이터 구조에 스트림 데이터 매핑

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

int iReadData = pMyStrct->Item2; 

는 C++ 방법입니다 : C에서 는 ++이 단순히 내가 (반대 또는 반대의 경우도 마찬가지) 예를 사용하고자하는 데이터 형식으로 스트림에 대한 포인터를 캐스팅의 문제 일 것입니다 들어오는 데이터를 읽을 때 스트림 데이터의 품질을 확신하지 못하면 꽤 안전하지 않지만 보내는 데이터는 빠르고 쉽습니다.

답변

16

대부분의 사람들은 (빨리 진 속도가 느린 XML 포맷터가, 둘 다 반사에 따라 달라집니다 및 버전 어느 정도 관대) 당신이 가장 빠른 (안전하지 않은)를 원하는 경우,

그러나 .NET 직렬화를 사용 방법 - 왜 안 :

쓰기 :

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

경우 양쪽의 그물 :

은 바이너리 직렬화를 사용하고 바이트 [] 결과를 보내한다고 생각합니다.

구조를 완전히 blittable로 신뢰하는 것은 문제가 될 수 있습니다.

약간의 오버 헤드 (CPU와 네트워크 모두)로 지불하지만 안전 할 것입니다.

2

각 멤버 변수를 수동으로 채워야하는 경우 FormatterServices를 사용하여 개체와 관련된 변수 형식 목록을 순서대로 검색하여 기본 요소가 염려되는 한 조금씩 일반화 할 수 있습니다. 스트림에서 다른 메시지 유형이 많이 나오는 프로젝트에서이 작업을 수행해야했으며 각 메시지에 대해 serializer/deserializer를 쓰고 싶지 않았습니다.

다음은 바이트 []에서 비 직렬화를 일반화하는 데 사용한 코드입니다.

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

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