2014-06-20 20 views
7

Tôi có một đối tượng đơn giản mà trông như thế này:Fixed Object để Byte mảng

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

Tôi đã thử mã này tôi thấy ở đâu đó trên mạng:

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

Nhưng bằng cách nào đó các mảng byte trở về có kích thước 248 byte.
Tôi hy vọng nó sẽ là 4 byte x 4 trường = 16 byte.

CÂU HỎI:
Cách sạch nhất để chuyển đổi đối tượng cố định thành mảng byte là gì?
Và mảng kết quả có phải là kích thước 16 byte trong trường hợp này không?

Trả lời

6

BinaryFormatter tiết kiệm rất nhiều loại thông tin để có thể deserialize đúng cách. Nếu bạn muốn serialization compact hoặc để comunicate qua một số giao thức nghiêm ngặt, bạn sẽ phải làm điều đó explictly như thế này:

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

ở đây tôi chuyển đổi mỗi UInt32 của bạn vào mảng byte và lưu nó trong kết quả mảng.

Sửa
Hóa ra có một cách khác bằng cách sử dụng structMarshal Trước tiên, bạn hãy struct và đánh dấu nó với các thuộc tính như thế:

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

    public int IntField; 
} 

Đây LayoutKind.Sequential nói với clr để giữ các lĩnh vực trong bộ nhớ theo thứ tự như tuyên bố. Nếu không có Pack = 1 cấu trúc có thể mất nhiều bộ nhớ hơn mức yêu cầu. Giống như struct với một trường short và một byte chỉ cần 3 byte, nhưng theo mặc định kích thước của nó sẽ là 4 (bộ xử lý có hướng dẫn thao tác đơn byte, 2 byte và 4 byte, clr hi sinh một byte cho mỗi thể hiện cấu trúc để giảm số lượng hướng dẫn về mã máy bằng một nửa). Bây giờ bạn có thể sử dụng Marshal để sao chép byte:

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

Mọi thứ hoạt động tốt với các loại đơn giản. Đối với các loại phức tạp như string, bạn sẽ phải sử dụng thuộc tính MarshalAs và việc sử dụng nó phức tạp hơn một chút (Ví dụ: tôi đã nói với clr để marshal chuỗi là mảng có kích thước cố định là 50 byte).

2

Hãy nhớ rằng khi bạn nối tiếp một đối tượng với BinaryFormatter, bạn sẽ bao gồm những thứ như nhập thông tin. Điều đó dẫn đến chi phí mà bạn đang thấy.

Nếu bạn muốn sắp xếp một đối tượng vào (trong trường hợp này) chỉ 16 byte thì bạn sẽ cần phải thực hiện thủ công với một cái gì đó như là StreamWriter.

Nếu kích thước không phải là vấn đề thì Binaryformatter trong trường hợp này không đúng và không tốn nhiều mã để thực hiện, nhưng đó không phải là cách hiệu quả nhất để thực hiện.

Edit: Đây là cách bạn muốn làm điều đó với một StreamWriter

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

Cần 16 byte. Bất kỳ đề xuất làm thế nào tôi nên sử dụng StreamWriter ?? –

+0

Tôi đã chỉnh sửa câu trả lời của mình. Mảng byte kết quả phải là 16 byte nếu bạn làm theo cách tôi đề xuất. Lưu ý rằng đây chỉ là mã mẫu. Tôi chưa thử nghiệm nó, cũng không sử dụng câu lệnh 'using' mà bạn nên sử dụng cho các đối tượng thực hiện' IDisposable' như 'MemoryStream'. Bạn có thể quyết định thực hiện chức năng này trong đối tượng Foo của bạn, vì vậy bạn có thể gọi foo.Serialize() để nhận mảng byte của bạn. –

2

Dưới đây là một cách để thực hiện thủ công, điều này sẽ đảm bảo 16 byte.

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

    } 
} 

Output:

16 
1 2 3 4 
2

Dưới đây là một cách khác ... đó là cách tốt nhất là một vấn đề quan điểm có thể. Tôi thích câu trả lời của Atomosk. Kết hợp câu trả lời đó với quá tải cast operator và bạn đã có một giải pháp khá thanh lịch.

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