2008-08-05 93 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(); 

閱讀:

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

如果雙方.NET:

認爲你應該使用二進制序列化和發送字節[]的結果。

信任你的結構是完全blittable可以麻煩。

你將支付一些開銷(包括CPU和網絡),但將是安全的。

2

如果您需要手工填充每個成員變量,則可以通過使用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; 
    } 
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; 
    } 
}