2011-02-01 9 views
92

Estoy escribiendo un prototipo de conexión TCP y tengo problemas para homogeneizar los datos que se enviarán.Convierta cualquier objeto en un byte []

Por el momento, estoy enviando nada más que cadenas, pero en el futuro queremos poder enviar cualquier objeto.

El código es bastante simple en el momento, porque pensé que todo podía ser echado en una matriz de bytes:

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

Esto por supuesto es bastante fácil de resolver con un

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

El problema es decir, si lo hago de esa manera, necesito verificar CADA tipo de objeto que no se puede convertir en un byte [] en tiempo de ejecución.

Dado que no conozco todos los objetos que no se pueden convertir en un byte [] en el tiempo de ejecución, esto realmente no es una opción.

¿Cómo se convierte un objeto en una matriz de bytes en C# .NET 4.0?

+2

Esto no es posible de ninguna manera significativa en general (considerar, por ejemplo, una instancia de 'FileStream', o cualquier objeto que encapsula un mango por el estilo). – jason

+2

¿Tiene la intención de tener todos los clientes ejecutando .NET? Si la respuesta es no, debería considerar alguna otra forma de serialización (XML, JSON o similares) –

Respuesta

139

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

Tenga en cuenta que obj y propiedades/campos dentro de obj (y por lo tanto-on para la totalidad de sus propiedades/campos) serán todos necesitan ser etiquetados con el Serializable attribute a ser éxito serializado con esto.

+9

Tenga cuidado con lo que hace con "cualquier" objeto del otro lado, ya que puede que ya no tenga sentido (por ejemplo, , si ese objeto fuera un identificador para un archivo, o similar) –

+0

Sí, se aplican advertencias normales, pero no es una mala idea recordarle a la gente. –

+17

Puede ser una buena idea ajustar el uso del MemoryStream en un bloque 'using', ya que liberará ansiosamente el buffer interno utilizado. –

12

Lo que está buscando es la serialización. Hay varias formas de serialización disponibles para la plataforma .NET

0

¿Qué hay de serialización? echar un vistazo here.

0

Prefiero usar la expresión "serialización" que "fundir en bytes". Serializar un objeto significa convertirlo en una matriz de bytes (o XML, u otra cosa) que se puede usar en el cuadro remoto para reconstruir el objeto. En .NET, el Serializable attribute marca los tipos cuyos objetos se pueden serializar.

Saludos, Matthias

1

podría utilizar el built-in serialization tools en el marco y serializar a un MemoryStream. Esta puede ser la opción más sencilla, pero podría producir un byte más grande [] que el estrictamente necesario para su escenario.

Si ese es el caso, puede utilizar la reflexión para iterar sobre los campos y/o propiedades en el objeto a serializar y escribirlos manualmente en MemoryStream, llamando a la serialización recursivamente si es necesario serializar tipos no triviales. Este método es más complejo y tomará más tiempo implementarlo, pero le permite un mayor control sobre la transmisión serializada.

71

pago y envío este artículo: http://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html

usar el siguiente código

// 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 para proporcionar métodos de codificación y descodificación. –

+4

Como se menciona en un comentario a [esta respuesta] (http://stackoverflow.com/a/4865123/1852105), el 'MemorySteam' debe estar envuelto en un bloque' using'. – rookie1024

+0

¿hay algo que deba respetar además?Lo implementé de esa manera y formateando un objeto que contiene 3 miembros públicos int32 resulta en una ByteArray de 244 bytes de longitud. ¿No estoy sabiendo algo sobre la sintaxis de C# o hay algo que probablemente pasaría por alto? – dhein

16

Como otros han dicho antes, se puede utilizar la serialización binaria, pero puede producir un bytes adicionales o deserializar en un objetos con no exactamente los mismos datos. Usar la reflexión por otro lado es bastante complicado y muy lento. Hay una otra solución que puede convertir estrictamente sus objetos de bytes y viceversa - de clasificación:

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

Y para convertir bytes de objetar:

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

Se es notablemente más lento y en parte peligroso usar este enfoque para objetos pequeños y estructuras en comparación con su propio campo de serialización por campo (debido a la doble copia de/a la memoria no administrada), pero es la forma más fácil de convertir el objeto a byte [] sin implementar la serialización y sin el atributo [Serializable].

+0

¿Por qué crees que 'StructureToPtr' +' Copy' es lento? ¿Cómo puede ser más lento que la serialización? ¿Hay alguna solución más rápida? –

+0

Si lo usa para estructuras pequeñas que constan de pocos tipos simples, sí (que es un caso bastante común), es lento debido a clasificación y copia cuádruple (de objeto a montón, de montón a bytes, de bytes a montón, de montón a objeto). Podría ser más rápido cuando se usa IntPtr en lugar de bytes, pero no en este caso. Y es más rápido para esos tipos escribir un serializador propio que simplemente pone valores en la matriz de bytes. No digo que sea más lento que la serialización incorporada ni que sea "demasiado lento". – Aberro

+1

Me gusta este método ya que mapea byte a byte. Este es un método realmente bueno para intercambiar memoria con el mapeo de C++. +1 para ti. –

1

camino alternativo para convertir objeto de matriz de bytes:

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

Intenté esto, no pareció funcionar para mí en .NET 4.6.1 y 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; 
    } 
} 

Usted puede usarlo como a continuación código.

 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

Una aplicación adicional, que utiliza Newtonsoft.Json JSON binario y no requiere marcar todo con el atributo [Serializable]. Solo un inconveniente es que un objeto debe estar envuelto en una clase anónima, por lo que el conjunto de bytes obtenido con la serialización binaria puede ser diferente de este.

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

clase anónima se utiliza porque BSON debe comenzar con una clase o matriz. No he tratado de deserializar byte [] de nuevo a objeto y no estoy seguro de si funciona, pero he probado la velocidad de conversión a byte [] y satisface completamente mis necesidades.

0

soluciones combinadas de la clase Extensiones:

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

¿Qué tal algo tan simple como este?

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