将任何对象转换为一个字节[]

我正在编写一个原型 TCP 连接,我有一些麻烦同质化数据发送。

目前,我只发送字符串,但在未来,我们希望能够发送任何对象。

目前代码非常简单,因为我认为所有的代码都可以转换为字节数组:

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[]'.
...
}

当然,使用

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

问题是,如果我这样做,我需要检查每一种类型的对象,不能在运行时强制转换为字节[]。

因为我不知道在运行时不能强制转换成 byte []的每个对象,所以这真的不是一个选项。

在 C # . NET 4.0中,如何将任何对象转换为字节数组?

311893 次浏览

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

请注意,objobj中的任何属性/字段(以及它们的所有属性/字段)都需要用 Serializable属性进行标记,以便成功地用这个序列化。

您要寻找的是序列化

我宁愿使用表达式“序列化”而不是“转换为字节”。序列化对象意味着将其转换为字节数组(或 XML 或其他内容) ,可以在远程框中使用该数组重新构造对象。进去。NET 中,Serializable属性标记其对象可序列化的类型。

您可以在框架中使用 内置的序列化工具并序列化为 记忆流。这可能是最简单的选项,但是可能会产生比您的场景严格需要的更大的字节[]。

如果是这种情况,您可以利用反射来迭代要序列化的对象中的字段和/或属性,并手动将它们写入 MemoryStream,如果需要序列化非平凡类型,则递归地调用序列化。此方法更为复杂,需要花费更多的时间来实现,但允许您对序列化流进行更多的控制。

检查这篇文章: http://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html

使用下面的代码

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

正如前面提到的,您可以使用二进制序列化,但是它可能会产生额外的字节,或者被反序列化为具有不完全相同数据的对象。另一方面,使用反射是相当复杂和非常缓慢的。 还有另一种解决方案,可以严格地将对象转换为字节,反之亦然——编组:

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

并将字节转换为对象:

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

与逐字段序列化相比,对小对象和结构使用这种方法显然更慢,而且部分不安全(因为从/到非托管内存的双重复制) ,但是这是最简单的方法,可以严格地将对象转换为 byte [] ,而不需要实现序列化,也不需要[ Serializer ]属性。

将对象转换为字节数组的替代方法:

TypeConverter objConverter = TypeDescriptor.GetConverter(objMsg.GetType());
byte[] data = (byte[])objConverter.ConvertTo(objMsg, typeof(byte[]));
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;
}
}

您可以像下面的代码那样使用它。

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

另一个实现是使用 牛顿软件,杰森二进制 JSON,并且不需要使用[ Serializer ]属性标记所有内容。只有一个缺点是对象必须包装在匿名类中,因此通过二进制序列化获得的字节数组可能与此不同。

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

使用匿名类是因为 BSON 应该以类或数组开始。 我没有尝试将 byte []反序列化回对象,也不确定它是否工作,但测试了转换到 byte []的速度,它完全满足了我的需要。

扩展类中的组合解决方案:

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


}

这么简单的东西怎么样?

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

使用 Encoding.UTF8.GetBytes比使用 MemoryStream快。 在这里,我使用 牛顿软 Json将输入对象转换为 JSON 字符串,然后从 JSON 字符串获取字节。

byte[] SerializeObject(object value) =>Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value));

@ Daniel DiPaolo 版本的基准

Method                    |     Mean |     Error |    StdDev |   Median |  Gen 0 | Allocated |
--------------------------|----------|-----------|-----------|----------|--------|-----------|
ObjectToByteArray         | 4.983 us | 0.1183 us | 0.2622 us | 4.887 us | 0.9460 |    3.9 KB |
ObjectToByteArrayWithJson | 1.548 us | 0.0309 us | 0.0690 us | 1.528 us | 0.3090 |   1.27 KB |