Не уверен что такое возможно, но есть ли хоть что-то что может помочь прочитать структуру данных как массив байт? Допустим в си/c++ проблем с таковым нет. Приводим указатель на структуру к типу const char*
и читаем данные из памяти основываясь на размере.
Это необходимо чтобы отправить структуру в виде массива байт удалённому серверу.
Отталкиваясь от последней фразы, напишу ответ. Для передачи данных на удаленный сервер обычно используется сериализация. Этот механизм превращает объект в простой тип для передачи по сети. Есть несколько вариантов:
В дополнение хочется сказать что для оптимизации пересылки пакетов через сеть лучше их предварительно сжимать. Особенно, если пересылка идёт через HTTP протокол, он поддерживает сжатие c помощью заголовков Accept-Encoding и Content-Encoding.
В дополнении к ответу @Gordory могу сказать следующее: при отправке данных по сети Вам по-любому придется работать с массивом байт. Так почему бы не взять блок памяти, отведенный под Вашу структуру, не скопировать его в byte[]
и не отправить на удаленный сервер, где позже также выгрузить в память?
В случае, если Вы можете полностью отказаться от ссылочных типов данных внутри Вашей структуры, могу предложить Вам именно такой вариант:
// Пример пакета
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public unsafe struct Packet
{
[FieldOffset(0)]
public int ID;
// Int32 занимает 4 байта
[FieldOffset(4)]
// Строка будет храниться внутри структуры в виде фиксированного массива указанной длины
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string Message;
public Packet(int ID, string Message)
{
this.ID = ID;
// В ANSI 1 символ занимает 1 байт
this.Message = Message.Length < 129 ? Message : throw new ArgumentException("Message is too long! Max length is 128 characters!", nameof(Message));
}
}
Расширение для демонстрации:
public static class StructExtensions
{
public static byte[] Zip<T>(this T Obj) where T : struct
{
// Получим размер, занимаемый объектом
int size = Marshal.SizeOf(Obj);
// Сюда будем читать данные
byte[] bytes = new byte[size];
// Выделим память и получим указатель на выделенный блок
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
// Пишем данные структуры в неуправляемый блок памяти
Marshal.StructureToPtr(Obj, ptr, false);
// Копируем данные
Marshal.Copy(ptr, bytes, 0, size);
}
catch (Exception exception)
{
// Даже в случае ошибки память следует освободить
Marshal.FreeHGlobal(ptr);
throw exception;
}
Marshal.FreeHGlobal(ptr);
return bytes;
}
// Не надо создавать расширения для стандартных типов
// Здесь я это делаю чисто ради наглядности примера
public static T Unzip<T>(this byte[] Bytes) where T : struct
{
// Получаем дексриптор (указываем, что он закреплен, чтобы сборщик мусора не баловался)
GCHandle handle = GCHandle.Alloc(Bytes, GCHandleType.Pinned);
try
{
// Читаем структуру из блока памяти
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
// Возвращаем ее
return theStructure;
}
catch (Exception exception)
{
// Даже в случае ошибки память следует освободить
handle.Free();
throw exception;
}
}
}
И сам тест:
// Инициализируем объект
int uid = 10;
string msg = "Hello, world!";
Packet packet = new Packet(uid, msg);
// Сериализуем его и тут же десериализуем обратно
Packet gotPacket = packet.Zip().Unzip<Packet>(); // ID == 10, Message == "Hello, world!"
Заметьте, что метод .Zip()
вернет нам массив байт длинной 132
(4 на int
и 128 на string
), то есть ровно столько, сколько мы и храним
Конечно, данные не сжимаются, но и никакого лишнего "мусора" не примешивается, как в случае с сериализацией с помощью System.Runtime.Serialization
Суть данного метода в том, что мы просто копируем участок памяти, занимаемый структурой, в массив байт. Потом этот массив мы можем точно так же записать в участок памяти, отведенный под аналогичную (хотя бы по размерам) структуру
Именно поэтому важно отказаться (в рамках данного метода) от ссылочных типов, ибо на их "месте" находятся указатели на иные блоки памяти, где и хранятся их данные. Тем самым в рамках одной и той же машины копирование структуры, которая включает в себя ссылочные типы (вроде массивов), пройдет успешно (если указатели на объекты не сменились), а вот при передаче такого блока на удаленную машину Вы получите результаты весьма неожиданные (точнее, неопределенные)
Плюсом (пусть и немного спорным) данного метода является то, что двум связанным сервисам не нужно иметь в своем арсенале одну и ту же структуру, взятую из одной и той же сборки! Им необходимо и достаточно знать лишь построение этой структуры, а далее можно хоть типы менять)!
К примеру, добавим к нашему прошлому тесту такую структуру:
// "Весит" те же 132 байта, что и Packet
public unsafe struct Packet2
{
// sizeof(int) == sizeof(uint)
public uint ID;
// Размер ANSI-строки в 128 символов равен 128 байтам
public fixed byte Message[128];
}
И дополним наш код считывания этими строчками:
Packet2 endDataPacket = packet.Zip().Unzip<Packet2>(); // ID == 10, Message == ['H', 'e', ..., '!', '\0', ..., '\0']
string test = new string((sbyte*)endDataPacket.Message); // Hello, world!
Как видите, все работает, пусть мы и описали не в точности такую структуру, какую использовали до этого)
Надеюсь, мой метод будет Вам полезен, а мой ответ помог Вам разобраться, каким образом средствами C#
можно считать структуру как массив байт)
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Если у меня есть модель данных в которой есть св-во представленное абстрактным классомЕсли я возвращаю ответ GET запросом, то JSON сериализатор...
Предположим у меня есть Button с привязкой к команде ViewModelВо ViewModel я пишу:
при создании новой строки я установил в ячейку подсказку с текстом с помощью XAML стиля FallbackValue=Name, цвет подсказки стандартный черный, возможно...