2011-02-01 9 views
92

Tôi đang viết một kết nối TCP nguyên mẫu và tôi gặp phải một số rắc rối khi đồng nhất dữ liệu được gửi đi.Chuyển đổi bất kỳ đối tượng nào thành một byte []

Hiện tại, tôi không gửi gì ngoài chuỗi, nhưng trong tương lai, chúng tôi muốn có thể gửi bất kỳ đối tượng nào.

Mã này là khá đơn giản vào lúc này, bởi vì tôi nghĩ rằng tất cả mọi thứ có thể được đúc thành một mảng byte:

void SendData(object headerObject, object bodyObject) 
{ 
    byte[] header = (byte[])headerObject; //strings at runtime, 
    byte[] body = (byte[])bodyObject;  //invalid cast exception 

    // Unable to cast object of type 'System.String' to type 'System.Byte[]'. 
    ... 
} 

Điều này tất nhiên là dễ dàng đủ giải quyết với một

if(state.headerObject is System.String){...} 

Vấn đề là, nếu tôi làm theo cách đó, tôi cần phải kiểm tra MỌI loại đối tượng mà không thể được đúc thành một byte [] khi chạy.

Vì tôi không biết mọi đối tượng không thể chuyển thành byte [] khi chạy, đây thực sự không phải là một tùy chọn.

Làm thế nào để chuyển đổi bất kỳ đối tượng nào thành một mảng byte trong C# .NET 4.0?

+2

Điều này là không thể theo bất kỳ cách nào có ý nghĩa nói chung (xem xét, ví dụ: một thể hiện của 'FileStream' hoặc bất kỳ đối tượng nào đóng gói một tay cầm như thế). – jason

+2

Bạn có dự định có tất cả các máy khách đang chạy .NET không? Nếu câu trả lời là không, bạn nên xem xét một số dạng tuần tự hóa khác (XML, JSON hoặc các dạng tương tự) –

Trả lời

139

Sử dụng BinaryFormatter:

byte[] ObjectToByteArray(object obj) 
{ 
    if(obj == null) 
     return null; 
    BinaryFormatter bf = new BinaryFormatter(); 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     bf.Serialize(ms, obj); 
     return ms.ToArray(); 
    } 
} 

Lưu ý rằng obj và bất kỳ thuộc tính/lĩnh vực trong obj (và cái trên cho tất cả các tài sản của họ/các lĩnh vực) tất cả sẽ cần phải được gắn thẻ với các Serializable attribute để được thành công được tuần tự hóa với điều này.

+9

Hãy cẩn thận với những gì bạn làm với đối tượng "bất kỳ" ở phía bên kia, vì nó có thể không còn ý nghĩa nữa (ví dụ , nếu đối tượng đó là một xử lý cho một tập tin, hoặc tương tự) –

+0

Yup, hãy áp dụng điều kiện bình thường, nhưng không phải là một ý tưởng tồi để nhắc nhở mọi người về chúng. –

+17

Có thể nên sử dụng MemoryStream trong một khối 'using', vì nó sẽ háo hức phát hành bộ đệm bên trong được sử dụng. –

12

Điều bạn đang tìm kiếm là tuần tự hóa. Có một số hình thức serialization sẵn cho nền tảng Net

0

Làm thế nào về serialization? hãy xem here.

0

Tôi muốn sử dụng cụm từ "tuần tự hóa" hơn là "truyền thành byte". Nối tiếp một đối tượng có nghĩa là chuyển đổi nó thành một mảng byte (hoặc XML, hoặc cái gì khác) có thể được sử dụng trên hộp điều khiển từ xa để xây dựng lại đối tượng. Trong .NET, Serializable attribute đánh dấu các loại có đối tượng có thể được tuần tự hóa.

Chúc mừng, Matthias

1

Bạn có thể sử dụng built-in serialization tools trong khuôn khổ và serialize đến một MemoryStream. Đây có thể là tùy chọn đơn giản nhất, nhưng có thể tạo ra một byte lớn hơn [] hơn là có thể cần thiết cho kịch bản của bạn.

Nếu trường hợp đó xảy ra, bạn có thể sử dụng phản xạ để lặp qua các trường và/hoặc thuộc tính trong đối tượng được tuần tự hóa và ghi chúng theo cách thủ công vào MemoryStream, gọi serialization đệ quy nếu cần để sắp xếp các loại không tầm thường. Phương pháp này phức tạp hơn và sẽ mất nhiều thời gian hơn để triển khai, nhưng cho phép bạn kiểm soát nhiều hơn luồng được tuần tự hóa.

71

kiểm tra bài viết này: http://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html

Sử dụng mã dưới đây

// Convert an object to a byte array 
private byte[] ObjectToByteArray(Object obj) 
{ 
    if(obj == null) 
     return null; 

    BinaryFormatter bf = new BinaryFormatter(); 
    MemoryStream ms = new MemoryStream(); 
    bf.Serialize(ms, obj); 

    return ms.ToArray(); 
} 

// Convert a byte array to an Object 
private Object ByteArrayToObject(byte[] arrBytes) 
{ 
    MemoryStream memStream = new MemoryStream(); 
    BinaryFormatter binForm = new BinaryFormatter(); 
    memStream.Write(arrBytes, 0, arrBytes.Length); 
    memStream.Seek(0, SeekOrigin.Begin); 
    Object obj = (Object) binForm.Deserialize(memStream); 

    return obj; 
} 
+12

+1 để cung cấp cả phương thức mã và giải mã. –

+4

Như đã đề cập trong một chú thích cho [câu trả lời này] (http://stackoverflow.com/a/4865123/1852105), 'MemorySteam' sẽ được gói trong một khối' using'. – rookie1024

+0

có bất kỳ điều gì tôi phải tôn trọng trong đơn yêu cầu không?Tôi thực hiện nó theo cách đó và định dạng một đối tượng có chứa 3 thành viên công cộng int32 kết quả trong một ByteArray dài Bytes 244. Tôi không biết điều gì đó về cú pháp C# hay có bất cứ điều gì tôi sẽ bỏ lỡ bằng cách sử dụng? – dhein

16

Giống như những người khác đã nói trước đó, bạn có thể sử dụng serialization nhị phân, nhưng nó có thể tạo ra một byte thêm hoặc được deserialized vào một đối tượng với không chính xác cùng một dữ liệu. Sử dụng phản xạ mặt khác khá phức tạp và rất chậm. Có một một giải pháp mà có thể chuyển đổi đúng đối tượng của bạn để byte và ngược lại - marshalling:

var size = Marshal.SizeOf(your_object); 
// Both managed and unmanaged buffers required. 
var bytes = new byte[size]; 
var ptr = Marshal.AllocHGlobal(size); 
// Copy object byte-to-byte to unmanaged memory. 
Marshal.StructureToPtr(your_object, ptr, false); 
// Copy data from unmanaged memory to managed buffer. 
Marshal.Copy(ptr, bytes, 0, size); 
// Release unmanaged memory. 
Marshal.FreeHGlobal(ptr); 

Và để chuyển đổi byte để phản đối:

var bytes = new byte[size]; 
var ptr = Marshal.AllocHGlobal(size); 
Marshal.Copy(bytes, 0, ptr, size); 
var your_object = (YourType)Marshal.PtrToStructure(ptr, typeof(YourType)); 
Marshal.FreeHGlobal(ptr); 

Đó là đáng kể chậm hơn và một phần không an toàn để sử dụng cách tiếp cận này cho các đối tượng và cấu trúc nhỏ so với trường tuần tự hóa của bạn theo trường (vì sao chép đôi từ/sang bộ nhớ không được quản lý), nhưng đó là cách dễ nhất để chuyển đổi đối tượng thành byte [] mà không thực hiện tuần tự hóa và không có thuộc tính [Serializable].

+0

Tại sao bạn nghĩ 'StructureToPtr' +' Copy' là chậm? Làm thế nào nó có thể chậm hơn serialization? Có giải pháp nào nhanh hơn không? –

+0

Nếu bạn sử dụng nó cho các cấu trúc nhỏ bao gồm một vài kiểu đơn giản, có (đó là trường hợp khá phổ biến), nó chậm vì sao chép và sao chép quad (từ đối tượng đến đống, từ heap sang byte, từ byte sang heap, từ đống để đối tượng). Nó có thể nhanh hơn khi IntPtr được sử dụng thay cho byte, nhưng không phải trong trường hợp này. Và nó nhanh hơn cho các loại như vậy để viết serializer riêng mà chỉ đơn giản là đặt các giá trị vào mảng byte. Tôi không nói rằng nó chậm hơn so với xây dựng trong serialization cũng không phải là nó "rất rất damn chậm". – Aberro

+1

Tôi thích phương pháp này vì nó ánh xạ từng byte một. Đây là một phương pháp thực sự tốt để trao đổi bộ nhớ với ánh xạ C++. 1 cho bạn. –

1

cách Alternative để chuyển đổi đối tượng để mảng byte:

TypeConverter objConverter = TypeDescriptor.GetConverter(objMsg.GetType()); 
    byte[] data = (byte[])objConverter.ConvertTo(objMsg, typeof(byte[])); 
+0

Đã thử cách này, có vẻ như nó không hoạt động đối với tôi trên .NET 4.6.1 và Windows 10. – Contango

5
public static class SerializerDeserializerExtensions 
{ 
    public static byte[] Serializer(this object _object) 
    { 
     byte[] bytes; 
     using (var _MemoryStream = new MemoryStream()) 
     { 
      IFormatter _BinaryFormatter = new BinaryFormatter(); 
      _BinaryFormatter.Serialize(_MemoryStream, _object); 
      bytes = _MemoryStream.ToArray(); 
     } 
     return bytes; 
    } 

    public static T Deserializer<T>(this byte[] _byteArray) 
    { 
     T ReturnValue; 
     using (var _MemoryStream = new MemoryStream(_byteArray)) 
     { 
      IFormatter _BinaryFormatter = new BinaryFormatter(); 
      ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream);  
     } 
     return ReturnValue; 
    } 
} 

Bạn có thể sử dụng nó như bên dưới mã.

 DataTable _DataTable = new DataTable(); 
     _DataTable.Columns.Add(new DataColumn("Col1")); 
     _DataTable.Columns.Add(new DataColumn("Col2")); 
     _DataTable.Columns.Add(new DataColumn("Col3")); 

     for (int i = 0; i < 10; i++) { 
      DataRow _DataRow = _DataTable.NewRow(); 
      _DataRow["Col1"] = (i + 1) + "Column 1"; 
      _DataRow["Col2"] = (i + 1) + "Column 2"; 
      _DataRow["Col3"] = (i + 1) + "Column 3"; 
      _DataTable.Rows.Add(_DataRow); 
     } 

     byte[] ByteArrayTest = _DataTable.Serializer(); 
     DataTable dt = ByteArrayTest.Deserializer<DataTable>(); 
0

Một thực hiện bổ sung, trong đó sử dụng Newtonsoft.Json JSON nhị phân và không cần đánh dấu tất cả mọi thứ với [Serializable] thuộc tính. Chỉ có một nhược điểm là một đối tượng phải được bọc trong lớp ẩn danh, vì vậy mảng byte thu được với tuần tự hóa nhị phân có thể khác với việc này.

public static byte[] ConvertToBytes(object obj) 
    { 
     using (var ms = new MemoryStream()) 
     { 
      using (var writer = new BsonWriter(ms)) 
      { 
       var serializer = new JsonSerializer(); 
       serializer.Serialize(writer, new { Value = obj }); 
       return ms.ToArray(); 
      } 
     } 
    } 

Lớp ẩn danh được sử dụng vì BSON phải bắt đầu bằng một lớp hoặc mảng. Tôi đã không cố gắng deserialize byte [] trở lại đối tượng và không chắc chắn nếu nó hoạt động, nhưng đã thử nghiệm tốc độ chuyển đổi thành byte [] và nó hoàn toàn đáp ứng nhu cầu của tôi.

0

Giải pháp kết hợp trong lớp Extensions:

public static class Extensions { 

    public static byte[] ToByteArray(this object obj) { 
     var size = Marshal.SizeOf(data); 
     var bytes = new byte[size]; 
     var ptr = Marshal.AllocHGlobal(size); 
     Marshal.StructureToPtr(data, ptr, false); 
     Marshal.Copy(ptr, bytes, 0, size); 
     Marshal.FreeHGlobal(ptr); 
     return bytes; 
    } 

    public static string Serialize(this object obj) { 
     return JsonConvert.SerializeObject(obj); 
    } 

} 
0

Làm thế nào về một cái gì đó đơn giản như thế này?

return ((object[])value).Cast<byte>().ToArray();