Как преобразовать объект в массив байтов в С#
У меня есть набор объектов, которые мне нужно записать в двоичный файл.
Мне нужно, чтобы байты в файле были компактными, поэтому я не могу использовать BinaryFormatter
. BinaryFormatter
генерирует различную информацию для нужд десериализации.
Если я попробую
byte[] myBytes = (byte[]) myObject
Я получаю исключение во время выполнения.
Мне нужно, чтобы это было быстро, поэтому я бы предпочел не копировать массивы байтов. Мне бы просто понравилось, что актер byte[] myBytes = (byte[]) myObject
работает!
ОК, чтобы быть ясным, я не могу иметь никаких метаданных в выходном файле. Просто байты объекта. Упакованный объект-объект. Основываясь на полученных ответах, похоже, что я напишу низкоуровневый код Buffer.BlockCopy
. Возможно, используя небезопасный код.
Ответы
Ответ 1
Преобразование объекта в массив байтов:
// Convert an object to a byte array
public static byte[] ObjectToByteArray(Object obj)
{
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
Вам просто нужно скопировать эту функцию в свой код и отправить ей объект, который вам нужно преобразовать в массив байтов. Если вам нужно снова преобразовать массив байтов в объект, вы можете использовать функцию ниже:
// Convert a byte array to an Object
public static Object ByteArrayToObject(byte[] arrBytes)
{
using (var memStream = new MemoryStream())
{
var binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
var obj = binForm.Deserialize(memStream);
return obj;
}
}
Вы можете использовать эту функцию с пользовательскими классами. Вам просто нужно добавить [Serializable] атрибут в свой класс, чтобы включить сериализацию
Ответ 2
Если вы хотите, чтобы сериализованные данные были действительно компактными, вы можете сами писать методы сериализации. Таким образом, у вас будет минимум накладных расходов.
Пример:
public class MyClass {
public int Id { get; set; }
public string Name { get; set; }
public byte[] Serialize() {
using (MemoryStream m = new MemoryStream()) {
using (BinaryWriter writer = new BinaryWriter(m)) {
writer.Write(Id);
writer.Write(Name);
}
return m.ToArray();
}
}
public static MyClass Desserialize(byte[] data) {
MyClass result = new MyClass();
using (MemoryStream m = new MemoryStream(data)) {
using (BinaryReader reader = new BinaryReader(m)) {
result.Id = reader.ReadInt32();
result.Name = reader.ReadString();
}
}
return result;
}
}
Ответ 3
Ну, отличное от myObject
до byte[]
никогда не будет работать, если у вас нет явного преобразования или если myObject
является byte[]
. Вам нужна структура сериализации. Там много, в том числе Protocol Buffers, который мне дорог и дорог. Это довольно "скудное и среднее" как по пространству, так и по времени.
Вы обнаружите, что почти все схемы сериализации имеют существенные ограничения на то, что вы можете сериализовать, однако - протокольные буферы больше, чем некоторые из-за кросс-платформенной.
Если вы можете предоставить больше требований, мы можем помочь вам больше, но это никогда не будет так просто, как литье...
EDIT: просто чтобы ответить на это:
Мне нужно, чтобы мой двоичный файл содержал байты объектов. Только байты, нет метаданные. уплотненный объекта к объекту. Так что я буду реализация пользовательской сериализации.
Пожалуйста, имейте в виду, что байты в ваших объектах довольно часто ссылаются... так что вам нужно будет разобраться, что с ними делать.
Я подозреваю, что вы обнаружите, что проектирование и внедрение собственной собственной схемы сериализации сложнее, чем вы себе представляете.
Я лично рекомендовал бы, если вам нужно сделать это только для нескольких конкретных типов, вы не потрудитесь придумать общую схему сериализации. Просто реализуйте метод экземпляра и статический метод во всех типах, которые вам нужны:
public void WriteTo(Stream stream)
public static WhateverType ReadFrom(Stream stream)
Одна вещь, которую нужно иметь в виду: все становится более сложным, если у вас есть наследование. Без наследования, если вы знаете, с какого типа вы начинаете, вам не нужно включать информацию о типе. Конечно, также есть вопрос о версировании - вам нужно беспокоиться о совместимости с предыдущими версиями и переходом на другие версии ваших типов?
Ответ 4
Вы действительно говорите о сериализации, которая может принимать различные формы. Поскольку вам нужны небольшие и двоичные, буферы протоколов могут быть жизнеспособным вариантом, что также допускает переносимость версий и переносимость. В отличие от BinaryFormatter
, формат протокола буферов протоколов не включает все метаданные типа; просто очень тонкие маркеры для идентификации данных.
В .NET есть несколько реализаций; в частности
Я смиренно утверждаю, что protobuf-net (который я написал) позволяет больше использовать .NET-идиоматику с типичными классами С# ( "обычные" буферы протокола имеют тенденцию требовать генерации кода); например:
[ProtoContract]
public class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
}
....
Person person = new Person { Id = 123, Name = "abc" };
Serializer.Serialize(destStream, person);
...
Person anotherPerson = Serializer.Deserialize<Person>(sourceStream);
Ответ 5
Я получил ответ Crystalonics и превратил их в методы расширения. Я надеюсь, что кто-то еще найдет их полезными:
public static byte[] SerializeToByteArray(this object obj)
{
if (obj == null)
{
return null;
}
var bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
public static T Deserialize<T>(this byte[] byteArray) where T : class
{
if (byteArray == null)
{
return null;
}
using (var memStream = new MemoryStream())
{
var binForm = new BinaryFormatter();
memStream.Write(byteArray, 0, byteArray.Length);
memStream.Seek(0, SeekOrigin.Begin);
var obj = (T)binForm.Deserialize(memStream);
return obj;
}
}
Ответ 6
Взгляните на Сериализация, метод "преобразования" всего объекта в поток байтов. Вы можете отправить его в сеть или записать в файл, а затем восстановить его позже.
Ответ 7
Чтобы получить доступ к памяти объекта напрямую (для выполнения "дампа ядра" ), вам нужно перейти в небезопасный код.
Если вам нужно что-то более компактное, чем BinaryWriter или дамп необработанного дамба, вы должны написать какой-то пользовательский код сериализации, который извлекает критическую информацию из объекта и оптимально упаковывает его.
изменить
Постскриптум Очень просто обернуть подход BinaryWriter в DeflateStream для сжатия данных, которые обычно примерно вдвое уменьшают размер данных.
Ответ 8
Я нашел другой способ преобразования объекта в байт []. Его решение:
IEnumerable en = (IEnumerable) myObject;
byte[] myBytes = en.OfType<byte>().ToArray();
Привет
Ответ 9
Это сработало для меня:
byte[] bfoo = (byte[])foo;
foo
- это объект, на котором я на 100% уверен, что это байтовый массив.
Ответ 10
Я считаю, что то, что вы пытаетесь сделать, невозможно.
Мусор, созданный BinaryFormatter
, необходим для восстановления объекта из файла после остановки программы.
Однако можно получить данные объекта, вам просто нужно знать точный размер (сложнее, чем это звучит):
public static unsafe byte[] Binarize(object obj, int size)
{
var r = new byte[size];
var rf = __makeref(obj);
var a = **(IntPtr**)(&rf);
Marshal.Copy(a, r, 0, size);
return res;
}
это можно восстановить через:
public unsafe static dynamic ToObject(byte[] bytes)
{
var rf = __makeref(bytes);
**(int**)(&rf) += 8;
return GCHandle.Alloc(bytes).Target;
}
Причина, по которой указанные выше методы не работают для сериализации, состоит в том, что первые четыре байта в возвращаемых данных соответствуют RuntimeTypeHandle
. RuntimeTypeHandle
описывает макет/тип объекта, но значение его изменяется при каждом запуске программы.
EDIT: это глупо, не делайте этого → Если вы уже знаете тип десериализованного объекта для определенного, вы можете переключить эти байты для BitConvertes.GetBytes((int)typeof(yourtype).TypeHandle.Value)
во время десериализации.
Ответ 11
Если у вас был только текст или что-то подобное хранилищу, вы могли бы сделать что-то вроде:
byte [] byteArray = Encoding.ASCII.GetBytes(myObject.text);
В противном случае вам придется сериализовать более привлекательный способ.
Ответ 12
Я знаю, что это старый пост, но он все еще появляется на вершине в google, поэтому я хотел добавить, как я это делаю. Мне нужно было передать изображение (в виде байта []) в словаре объектов. Вышеуказанные предложения не сработали, потому что хотя мой исходный массив сделал это с помощью методов ObjectToArray, преобразование изменило вывод на другой.
в конце концов, этот простой процесс работал у меня...
private object GetByteArray()
{
byte[] myReturn = new byte[] { 1, 2, 3, 4 };
return Convert.ToBase64String(myReturn);
}
private byte[] ConvertToByteArray(object myValues)
{
byte[] myReturn = Convert.FromBase64String(Convert.ToString(myValues));
return myReturn;
}