2014-06-20 4 views
7

Ich habe ein einfaches Objekt, das wie folgt aussieht:festes Objekt zu Byte-Array

public class Foo 
{ 
    public UInt32 One { get; set; } 
    public UInt32 Two { get; set; } 
    public UInt32 Three { get; set; } 
    public UInt32 Four { get; set; } 
} 

ich diesen Code habe ich versucht, irgendwo im Netz gefunden:

public byte[] ObjectToByteArray(Object obj) 
{ 
    MemoryStream fs = new MemoryStream(); 
    BinaryFormatter formatter = new BinaryFormatter(); 
    formatter.Serialize(fs, obj); 
    byte[] rval = fs.ToArray(); 
    fs.Close(); 
    return rval; 
} 

Aber irgendwie der zurückkehrende Byte-Array eine Größe von 248 Bytes.
Ich würde erwarten, dass es 4 Bytes x 4 Felder = 16 Bytes ist.

FRAGE:
Was der sauberste Weg ist ein festes Objekt in ein Byte-Array zu konvertieren?
Und sollte das resultierende Array in diesem Fall 16 Bytes groß sein?

Antwort

6

BinaryFormatter spart viel Typinformationen der Lage sein, richtig deserialisieren. Wenn Sie kompakte Serialisierung wollen oder über einen strengen Protokoll comunicate, werden Sie es zu tun haben, explictly wie folgt aus:

public byte[] ToByteArray() 
{ 
    List<byte> result = new List<byte>(); 

    result.AddRange(BitConverter.GetBytes(One)); 
    result.AddRange(BitConverter.GetBytes(Two)); 
    result.AddRange(BitConverter.GetBytes(Three)); 
    result.AddRange(BitConverter.GetBytes(Four)); 

    return result.ToArray(); 
} 

hier ich jedes Ihrer UInt32 in Byte-Array umwandeln und speichern sie in resultierende Array.

bearbeiten
Es stellte sich heraus dort ist eine andere Art von struct und Marshal Zuerst verwenden Sie struct und markieren es mit Attributen wie das machen:

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
struct MyStruct 
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)] 
    public string StringField; 

    public int IntField; 
} 

Hier LayoutKind.Sequential Tells clr zu halten Felder im Speicher in der gleichen Reihenfolge wie die Deklaration. Ohne Pack = 1 können Strukturen mehr Speicher belegen als erforderlich. Wie struct mit einem short Feld und einem byte braucht nur 3 Bytes, aber standardmäßig ist die Größe wird wahrscheinlich 4 (Prozessor verfügt über Anweisungen zum Bearbeiten von einzelnen Byte, 2 Bytes und 4 Bytes, Clr opfert ein Byte pro Instanz von struct, um Menge zu reduzieren der Anweisungen des Maschinencodes um die Hälfte). Jetzt können Sie Marshal verwenden Bytes zu kopieren:

public static byte[] GetBytes<T>(T str) 
{ 
    int size = Marshal.SizeOf(str); 
    var bytes = new byte[size]; 
    IntPtr ptr = Marshal.AllocHGlobal(size); 

    try 
    { 
     Marshal.StructureToPtr(str, ptr, true); 
     Marshal.Copy(ptr, bytes, 0, size); 
     return bytes; 
    } 
    finally 
    { 
     Marshal.FreeHGlobal(ptr); 
    } 
} 

Alles funktioniert gut mit einfachen Typen. Für komplexe Typen wie string müssen Sie MarshalAs Attribut verwenden und es ist ein bisschen komplizierter (In Beispiel ich sagte Clr zu Marshal String als Array von festen 50 Bytes Größe).

2

Denken Sie daran, wenn Sie ein Objekt mit dem BinaryFormatter serialisieren, schließen Sie Dinge wie Typinformationen ein. Das führt zu dem Overhead, den Sie sehen.

Wenn Sie ein Objekt in (in diesem Fall) nur 16 Bytes serialisieren möchten, müssen Sie es manuell mit etwas wie einem StreamWriter tun.

Wenn die Größe kein Problem ist, dann ist ein Binaryformatter in diesem Fall nicht falsch und benötigt nicht viel Code, aber es ist nicht der speicherfreundlichste Weg, dies zu tun.

Edit: Hier ist, wie Sie es mit einem StreamWriter tun würde

System.IO.MemoryStream stream = new System.IO.MemoryStream(); 

StreamWriter writer = new StreamWriter(stream); 

writer.Write(myObject.One); // here's where we actually write the data to the stream 
writer.Write(myObject.Two); 
writer.Write(myObject.Three); 
writer.Write(myObject.Four);  

writer.Flush(); // make sure all the data in the stream writer ends up in the 
        // underlying stream 

byte[] result = stream.ToArray(); // here's your resulting byte array 

stream.Dispose(); // don't forget to dispose of the stream!   
+0

Es muss 16 Bytes ja sein. Irgendwelche Vorschläge wie soll ich den StreamWriter verwenden? –

+0

Ich habe meine Antwort bearbeitet. Das resultierende Byte-Array sollte 16 Byte sein, wenn Sie es so machen, wie ich es vorschlage. Beachten Sie, dass dies nur ein Beispielcode ist. Ich habe es nicht getestet, noch verwendet es eine 'using'-Anweisung, die Sie für Objekte verwenden sollten, die 'IDisposable' wie' MemoryStream' implementieren. Sie könnten dies zu einer Funktion innerhalb Ihres Foo-Objekts machen, so dass Sie foo.Serialize() aufrufen können, um Ihr Byte-Array zu erhalten. –

2

Hier ist eine Möglichkeit, es manuell zu tun, die die 16-Byte garantiert.

using System; 
using System.IO; 
using System.Linq; 

public class Foo 
{ 
    public UInt32 One { get; set; } 
    public UInt32 Two { get; set; } 
    public UInt32 Three { get; set; } 
    public UInt32 Four { get; set; } 

    public Foo() {} 

    public Foo(byte[] array) 
    { 
     One = BitConverter.ToUInt32(array,0);  
     Two = BitConverter.ToUInt32(array,4); 
     Three = BitConverter.ToUInt32(array,8);  
     Four = BitConverter.ToUInt32(array,12);  
    } 
    public byte[] toByteArray() 
    { 
     byte[] retVal = new byte[16]; 
     byte[] b = BitConverter.GetBytes(One); 
     Array.Copy(b, 0, retVal, 0, 4); 
     b = BitConverter.GetBytes(Two); 
     Array.Copy(b, 0, retVal, 4, 4); 
     b = BitConverter.GetBytes(Three); 
     Array.Copy(b, 0, retVal, 8, 4); 
     b = BitConverter.GetBytes(Four); 
     Array.Copy(b, 0, retVal, 12, 4); 
     return retVal; 
    } 
} 
public class P{ 
    public static void Main(string[] args) { 
     Foo foo = new Foo(); 
     foo.One = 1; 
     foo.Two = 2; 
     foo.Three = 3; 
     foo.Four = 4; 

     byte[] arr = foo.toByteArray(); 
     Console.WriteLine(arr.Length); 


     Foo bar = new Foo(arr); 
     Console.WriteLine(string.Format("{0} {1} {2} {3}", bar.One, bar.Two, bar.Three, bar.Four)); 

    } 
} 

Ausgang:

16 
1 2 3 4 
2

Hier ist eine andere Art und Weise ... die Art und Weise am besten ist wohl Ansichtssache ist. Ich mag Atomosks Antwort. Kombinieren Sie diese Antwort mit den Überladungen der Darsteller, und Sie haben eine ziemlich elegante Lösung.

class Foo 
{ 
    public UInt32 One { get; set; } 
    public UInt32 Two { get; set; } 
    public UInt32 Three { get; set; } 
    public UInt32 Four { get; set; } 

    static public implicit operator byte[](Foo f) 
    { 
     MemoryStream m = new MemoryStream(16); 
     BinaryWriter w = new BinaryWriter(m); 

     w.Write(f.One); 
     w.Write(f.Two); 
     w.Write(f.Three); 
     w.Write(f.Four); 
     w.Close(); 
     m.Close(); 

     return m.ToArray(); 
    } 

    static public explicit operator Foo(byte[] b) 
    { 
     Foo f = new Foo(); 
     MemoryStream m = new MemoryStream(b); 
     BinaryReader r = new BinaryReader(m); 

     f.One = r.ReadUInt32(); 
     f.Two = r.ReadUInt32(); 
     f.Three = r.ReadUInt32(); 
     f.Four = r.ReadUInt32(); 

     r.Close(); 
     m.Close(); 

     return f; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Foo f = new Foo() { One = 1, Two = 2, Three = 3, Four = 4 }; 
     byte[] b = (byte[])f; 
     Console.WriteLine(b.Length); 

     f = (Foo)b; 
     Console.WriteLine("{0} {1} {2} {3}", f.One, f.Two, f.Three, f.Four); 

     Console.ReadKey(true); 
    } 
}