Подтвердить что ты не робот

Как преобразовать объект в массив байтов в С#

У меня есть набор объектов, которые мне нужно записать в двоичный файл.

Мне нужно, чтобы байты в файле были компактными, поэтому я не могу использовать BinaryFormatter. BinaryFormatter генерирует различную информацию для нужд десериализации.

Если я попробую

byte[] myBytes = (byte[]) myObject 

Я получаю исключение во время выполнения.

Мне нужно, чтобы это было быстро, поэтому я бы предпочел не копировать массивы байтов. Мне бы просто понравилось, что актер byte[] myBytes = (byte[]) myObject работает!

ОК, чтобы быть ясным, я не могу иметь никаких метаданных в выходном файле. Просто байты объекта. Упакованный объект-объект. Основываясь на полученных ответах, похоже, что я напишу низкоуровневый код Buffer.BlockCopy. Возможно, используя небезопасный код.

4b9b3361

Ответ 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;
    }