Как преобразовать структуру в массив байтов в C #? [Дубликат]

Сообщество Vue обычно предпочитает использовать Vuex для решения этой проблемы. Изменения приводятся к состоянию Vuex, и представление DOM просто вытекает из этого, устраняя необходимость в событиях во многих случаях.

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

Ниже приведен мой первоначальный ответ на этот вопрос, и это не тот подход, который я бы принял сейчас , имея больше опыта с Vue.


Это случай, когда я могу не согласиться с выбором дизайна Vue и прибегать к DOM.

В grand-child,

methods: {
    doEvent() { 
        try {
            this.$el.dispatchEvent(new Event("eventtriggered"));
        } catch (e) {
            // handle IE not supporting Event constructor
            var evt = document.createEvent("Event");
            evt.initEvent("eventtriggered", true, false);
            this.$el.dispatchEvent(evt);
        }
    }
}

и в parent,

mounted(){
    this.$el.addEventListener("eventtriggered", () => this.performAction())
}

В противном случае вы должны переизлучить или использовать шину.

Примечание: я добавил код в методе doEvent для обработки IE; этот код можно извлечь многоразовым способом.

66
задан Peter Mortensen 18 January 2013 в 16:57
поделиться

13 ответов

Это довольно просто, используя сортировку.

Верх файла

using System.Runtime.InteropServices

Функция

byte[] getBytes(CIFSPacket str) {
    int size = Marshal.SizeOf(str);
    byte[] arr = new byte[size];

    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(str, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

И для ее возврата:

CIFSPacket fromBytes(byte[] arr) {
    CIFSPacket str = new CIFSPacket();

    int size = Marshal.SizeOf(str);
    IntPtr ptr = Marshal.AllocHGlobal(size);

    Marshal.Copy(arr, 0, ptr, size);

    str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType());
    Marshal.FreeHGlobal(ptr);

    return str;
}

В вашей структуре вам нужно будет поставить это перед строкой

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;

И убедитесь, что SizeConst имеет такую ​​же большую возможную строку.

И вы должны, вероятно, прочитать это: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

98
ответ дан Vincent McNabb 20 August 2018 в 20:40
поделиться
  • 1
    Спасибо Винсет. GetBytes () следует вызывать после отправки байта [] ?? и frombytes () отправляет байты? Я немного растерянный приятель? – Swapnil Gupta 19 July 2010 в 08:11
  • 2
    – Vincent McNabb 19 July 2010 в 08:24
  • 3
    @Swapnil Это еще один вопрос, который вы должны задать отдельно. Вы должны рассмотреть возможность завершения нескольких обучающих программ СЕ по сокетам. Просто найдите Google. – Vincent McNabb 19 July 2010 в 11:13
  • 4
    В вашем методе fromBytes нет необходимости выделять CIFSPacket дважды. Marshal.SizeOf с радостью примет тип как параметр, а Marshal.PtrToStructure назначит новый управляемый объект. – Schneider 22 January 2014 в 17:47
  • 5
    Обратите внимание, что в некоторых случаях функция «StructureToPtr» выдает исключение. Это можно было бы зафиксировать с передачей «false» вместо «true» на Marshal.StructureToPtr(str, ptr, false);. Но нужно упомянуть, что я использую функции, завернутые в общий, хотя ... – Hi-Angel 10 October 2014 в 14:10

Это можно сделать очень просто.

Определите свою структуру явно с помощью [StructLayout(LayoutKind.Explicit)]

int size = list.GetLength(0);
IntPtr addr = Marshal.AllocHGlobal(size * sizeof(DataStruct));
DataStruct *ptrBuffer = (DataStruct*)addr;
foreach (DataStruct ds in list)
{
    *ptrBuffer = ds;
    ptrBuffer += 1;
}

Этот код может быть написан только в небезопасном контексте. Вы должны освободить addr, когда закончите с ним.

Marshal.FreeHGlobal(addr);
1
ответ дан aboveyou00 20 August 2018 в 20:40
поделиться
  • 1
    При выполнении явных упорядоченных операций в коллекции фиксированного размера вы, вероятно, должны использовать массив и for-loop. Массив, потому что это фиксированный размер, и for-loop, потому что foreach не гарантируется в ожидаемом порядке, если вы не знаете базовую реализацию вашего типа списка и его перечислителя и что он никогда не изменится. Например, можно было бы перечислить счетчик, чтобы начать с конца и, например, вернуться назад. – Physics-Compute 14 February 2017 в 22:50

Я бы посмотрел на классы BinaryReader и BinaryWriter. Недавно мне пришлось сериализовать данные в массив байтов (и обратно) и только найти эти классы после того, как я сам их переписал сам.

http://msdn.microsoft.com/en- us / library / system.io.binarywriter.aspx

На этой странице также есть хороший пример.

0
ответ дан AndrewS 20 August 2018 в 20:40
поделиться

Посмотрите на эти методы:

byte [] StructureToByteArray(object obj)
{
    int len = Marshal.SizeOf(obj);

    byte [] arr = new byte[len];

    IntPtr ptr = Marshal.AllocHGlobal(len);

    Marshal.StructureToPtr(obj, ptr, true);

    Marshal.Copy(ptr, arr, 0, len);

    Marshal.FreeHGlobal(ptr);

    return arr;
}

void ByteArrayToStructure(byte [] bytearray, ref object obj)
{
    int len = Marshal.SizeOf(obj);

    IntPtr i = Marshal.AllocHGlobal(len);

    Marshal.Copy(bytearray,0, i,len);

    obj = Marshal.PtrToStructure(i, obj.GetType());

    Marshal.FreeHGlobal(i);
}

Это бесстыдная копия другого потока, который я нашел в Googling!

Update: Для получения дополнительной информации проверьте источник

20
ответ дан Botz3000 20 August 2018 в 20:40
поделиться
  • 1
    @Abdel: Почему Shamless Copy? – Swapnil Gupta 19 July 2010 в 07:41
  • 2
    -1, вы должны предоставить ссылку на исходный поток. – Alastair Pitts 19 July 2010 в 07:44
  • 3
    У меня есть преобразование структуры в байтовый массив с помощью Marshalling теперь, как я могу проверить, получаю ли я ответ от сокета? Как это проверить? – Swapnil Gupta 19 July 2010 в 07:45
  • 4
    @Alastair, я пропустил это! Спасибо, что указали это. Я обновил свой ответ. – Abdel Raoof 19 July 2010 в 07:48
  • 5
    Эта опция зависит от платформы. Позаботьтесь о Grand Endian и Little endian и о 32 бит / 64 бит. – x77 19 July 2010 в 08:57

Я придумал другой подход, который мог бы преобразовать любой struct без хлопот фиксирующей длины, однако полученный массив байтов имел бы немного больше накладных расходов.

Вот пример struct:

[StructLayout(LayoutKind.Sequential)]
public class HelloWorld
{
    public MyEnum enumvalue;
    public string reqtimestamp;
    public string resptimestamp;
    public string message;
    public byte[] rawresp;
}

Как вы можете видеть, для всех этих структур потребуется добавить атрибуты фиксированной длины. Который часто мог занять больше места, чем требовалось. Обратите внимание, что требуется LayoutKind.Sequential, так как мы хотим, чтобы отражение всегда давало нам тот же порядок, когда тянул за FieldInfo. Мое вдохновение - от TLV Type-Length-Value. Давайте посмотрим на код:

public static byte[] StructToByteArray<T>(T obj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
        foreach (FieldInfo info in infos)
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream inms = new MemoryStream()) {

                bf.Serialize(inms, info.GetValue(obj));
                byte[] ba = inms.ToArray();
                // for length
                ms.Write(BitConverter.GetBytes(ba.Length), 0, sizeof(int));

                // for value
                ms.Write(ba, 0, ba.Length);
            }
        }

        return ms.ToArray();
    }
}

Вышеупомянутая функция просто использует BinaryFormatter для сериализации неизвестного размера raw object, и я просто отслеживаю размер и сохраняю его внутри выхода MemoryStream тоже.

public static void ByteArrayToStruct<T>(byte[] data, out T output)
{
    output = (T) Activator.CreateInstance(typeof(T), null);
    using (MemoryStream ms = new MemoryStream(data))
    {
        byte[] ba = null;
        FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
        foreach (FieldInfo info in infos)
        {
            // for length
            ba = new byte[sizeof(int)];
            ms.Read(ba, 0, sizeof(int));

            // for value
            int sz = BitConverter.ToInt32(ba, 0);
            ba = new byte[sz];
            ms.Read(ba, 0, sz);

            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream inms = new MemoryStream(ba))
            {
                info.SetValue(output, bf.Deserialize(inms));
            }
        }
    }
}

Когда мы хотим преобразовать его обратно в исходное struct, мы просто прочтем длину и вернем его обратно в BinaryFormatter, который в свою очередь сбрасывает его обратно в struct .

Эти 2 функции являются общими и должны работать с любым struct, я протестировал вышеуказанный код в моем проекте C#, где у меня есть сервер и клиент, подключенный и обменивающийся через NamedPipeStream и я пересылаю свой struct в виде массива байтов от одного и к другому и преобразовываю его обратно.

Я считаю, что мой подход может быть лучше, поскольку он не фиксирует длину самого struct и единственные накладные расходы - это всего лишь int для каждого поля, которое у вас есть в вашей структуре. Внутри байтового массива, генерируемого BinaryFormatter, есть немного незначительных накладных расходов, но кроме этого, это немного.

1
ответ дан codenamezero 20 August 2018 в 20:40
поделиться

@Abdel Olakara ответ donese не работает в .net 3.5, следует изменить следующим образом:

    public static void ByteArrayToStructure<T>(byte[] bytearray, ref T obj)
    {
        int len = Marshal.SizeOf(obj);
        IntPtr i = Marshal.AllocHGlobal(len);
        Marshal.Copy(bytearray, 0, i, len);
        obj = (T)Marshal.PtrToStructure(i, typeof(T));
        Marshal.FreeHGlobal(i);
    }
0
ответ дан lsaturn 20 August 2018 в 20:40
поделиться

Если вы действительно хотите, чтобы он был FAST, вы можете сделать это, используя небезопасный код с CopyMemory. CopyMemory примерно в 5 раз быстрее (например, 800 МБ данных занимает 3 с для копирования через маршаллинг, а только для копирования с помощью CopyMemory .6s). Этот метод ограничивает использование только данных, которые фактически хранятся в самой структуре struct blob, например. числа или массивы байтов с фиксированной длиной.

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    private static unsafe extern void CopyMemory(void *dest, void *src, int count);

    private static unsafe byte[] Serialize(TestStruct[] index)
    {
        var buffer = new byte[Marshal.SizeOf(typeof(TestStruct)) * index.Length];
        fixed (void* d = &buffer[0])
        {
            fixed (void* s = &index[0])
            {
                CopyMemory(d, s, buffer.Length);
            }
        }

        return buffer;
    }
21
ответ дан Paul 20 August 2018 в 20:40
поделиться

В качестве основного ответа используется тип CIFSPacket, который не является (или больше не доступен) на C #, я написал правильные методы:

    static byte[] getBytes(object str)
    {
        int size = Marshal.SizeOf(str);
        byte[] arr = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(str, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);

        return arr;
    }

    static T fromBytes<T>(byte[] arr)
    {
        T str = default(T);

        int size = Marshal.SizeOf(str);
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(arr, 0, ptr, size);

        str = (T)Marshal.PtrToStructure(ptr, str.GetType());
        Marshal.FreeHGlobal(ptr);

        return str;
    }

Протестировано, они работают.

4
ответ дан pbies 20 August 2018 в 20:40
поделиться
        Header header = new Header();
        Byte[] headerBytes = new Byte[Marshal.SizeOf(header)];
        Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);

Это должно сделать трюк быстро, не так ли?

0
ответ дан Ryan Brown 20 August 2018 в 20:40
поделиться

Похож на предопределенную структуру (C level) для некоторой внешней библиотеки. Маршал - ваш друг. Проверьте:

http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx

для стартера, как бороться с этим. Обратите внимание, что вы можете - с атрибутами - определять такие вещи, как макет макета и обработка строк. На самом деле очень хороший подход.

Для этого не выполняются ни BinaryFormatter, ни MemoryStream.

0
ответ дан TomTom 20 August 2018 в 20:40
поделиться
2
ответ дан x77 20 August 2018 в 20:40
поделиться
7
ответ дан xanatos 20 August 2018 в 20:40
поделиться

Этот пример применим только к чистым типам blittable, например типам, которые могут быть memcpy'd непосредственно в C.

Пример - хорошо известная 64-битная структура

[StructLayout(LayoutKind.Sequential)]  
public struct Voxel
{
    public ushort m_id;
    public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom;
}

Определено точно так же, структура будет автоматически упакована как 64-разрядная.

Теперь мы можем создать объем вокселей:

Voxel[,,] voxels = new Voxel[16,16,16];

И сохранить их все в байт array:

int size = voxels.Length * 8; // Well known size: 64 bits
byte[] saved = new byte[size];
GCHandle h = GCHandle.Alloc(voxels, GCHandleType.Pinned);
Marshal.Copy(h.AddrOfPinnedObject(), saved, 0, size);
h.Free();
// now feel free to save 'saved' to a File / memory stream.

Однако, поскольку OP хочет знать, как преобразовать структуру, наша структура Voxel может иметь следующий метод ToBytes:

byte[] bytes = new byte[8]; // Well known size: 64 bits
GCHandle h = GCHandle.Alloc(this, GCHandleType.Pinned);
Marshal.Copy(hh.AddrOfPinnedObject(), bytes, 0, 8);
h.Free();
0
ответ дан Петър Петров 20 August 2018 в 20:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: