И никак не найду гибкость, которая была у C/C++. Или ее там нет?
Принимаю из сокета пакет двоичных данных имеющих такую структуру:
struct CDataPacket
{
char DevType; // тип устройства
BYTE InfType; // тип информации
BYTE Length; // длина
BYTE Data[255]; // данные
};
Сразу же сталкиваюсь с тем, что в C# повторить ее нельзя: здесь массивы всегда ссылочного типа. Покопавшись, все-таки нахожу что массив можно сделать fixed. Правда, из-за него одного весь проект приходится делать unsafe.
Но главная проблема дальше. Данные принимаются в массив байт. В Си я создавал указатель типа CDataPacket, указывал им на начало массива и работал с ним. Здесь указателей нет, данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно? Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных? Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Здравствуйте, 777777w, Вы писали:
7>Сразу же сталкиваюсь с тем, что в C# повторить ее нельзя: здесь массивы всегда ссылочного типа. Покопавшись, все-таки нахожу что массив можно сделать fixed. Правда, из-за него одного весь проект приходится делать unsafe.
Почему не воспользоваться Intptr?
7> Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных?
Здравствуйте, IncremenTop, Вы писали:
7>>Сразу же сталкиваюсь с тем, что в C# повторить ее нельзя: здесь массивы всегда ссылочного типа. Покопавшись, все-таки нахожу что массив можно сделать fixed. Правда, из-за него одного весь проект приходится делать unsafe.
IT>Почему не воспользоваться Intptr?
A platform-specific type that is used to represent a pointer or a handle.
А чем он здесь поможет?
7>> Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных?
IT>Т.е. про десериализацию вы не слышали?
Только в MFC
А где об это можно почитать? У Рихтера это слово встречается, но так, будто читатель его уже знает.
Здравствуйте, 777777w, Вы писали:
7>И никак не найду гибкость, которая была у C/C++. Или ее там нет?
7> ...
7>Но главная проблема дальше. Данные принимаются в массив байт. В Си я создавал указатель типа CDataPacket, указывал им на начало массива и работал с ним. Здесь указателей нет, данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно? Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных? Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Здравствуйте, 777777w, Вы писали:
7>Принимаю из сокета пакет двоичных данных
Вам нужно почитать главу про маршалинг. C# — управляемый язык (читай "не даст выстрелить в ногу"). Можно конечно использовать fixed, но лучше не надо.
7>данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно?
См. сериализация. Это процес превращения экземпляра обьекта в нечто другое (xml, json, etc.) и наоборот (десериализация).
7>двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных?
Обьявите эти структуры правильно, напишите вот такой метод:
public static T ByteToStructure<T>(byte[] data)
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
T obj = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return obj;
}
> Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Все произойдет автоматически, если правильно обьявить структуры, см. StructLayoutAttribute, FieldOffsetAttribute, MarshalAsAttribute, etc.
Здравствуйте, 777777w, Вы писали:
7>Но главная проблема дальше. Данные принимаются в массив байт. В Си я создавал указатель типа CDataPacket, указывал им на начало массива и работал с ним. Здесь указателей нет, данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно? Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных? Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Указатели есть, у них, внезапно, даже синтаксис такой же как в C/C++, но разметить код unsafe-ами таки придется.
Реинтерпретация внутренностей структуры (аналог union) также есть, но она тонкая ручная с помощью см. StructLayoutAttribute и FieldOffsetAttribute.
Здравствуйте, 777777w, Вы писали:
7>И никак не найду гибкость, которая была у C/C++. Или ее там нет?
C# язык более высокого уровня, у него своя атмосфера свои абстракции.
7>Принимаю из сокета пакет двоичных данных имеющих такую структуру: 7>Сразу же сталкиваюсь с тем, что в C# повторить ее нельзя: здесь массивы всегда ссылочного типа. Покопавшись, все-таки нахожу что массив можно сделать fixed. Правда, из-за него одного весь проект приходится делать unsafe.
Э, а этот сокет может вернуть поток данных (Stream)? Открываешь поток, читаешь из него байты и парсишь их в соответствии со своей структурой. Может быть, в .NET есть уже код для этого — какое-нибудь быстрое наложение структуры на массив байт — гуглишь, экспериментируешь.
7>Но главная проблема дальше. Данные принимаются в массив байт. В Си я создавал указатель типа CDataPacket, указывал им на начало массива и работал с ним. Здесь указателей нет, данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно? Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных? Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Копировать. Прочитал массив байт, быстро проанализировал его тип (проверил magic numbers), создал структуру нужного типа — заполнил её данными из массива. Управлению памятью здесь — не твоя забота, оставь это сборщику мусора. Твоя задача писать простой и понятный код, а не накладывать универсальные структуры на произвольные участки памяти.
Здравствуйте, Regular_Man, Вы писали:
R_M>На самом деле все есть. Вот здесь, например, можно подробно почитать https://habrahabr.ru/post/114953/
Чувак там скромно умалчивает о том, что структуры в шарпе нельзя выделить в куче, и все эти fixed arrays лягут на стэке. Да и есть в шарпе, например нет struct union — именно они наибольшую головную боль вызывают. Я каждый раз, когда сталкиваюсь с Interop'ом задумываюсь о том, чтобы его писать на C++ CLI. Из-за вот такого, и из-за списков строк.
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, 777777w, Вы писали:
7>И никак не найду гибкость, которая была у C/C++. Или ее там нет?
7>Принимаю из сокета пакет двоичных данных имеющих такую структуру: 7>
7>struct CDataPacket
7> {
7> char DevType; // тип устройства
7> BYTE InfType; // тип информации
7> BYTE Length; // длина
7> BYTE Data[255]; // данные
7> };
7>
7>Сразу же сталкиваюсь с тем, что в C# повторить ее нельзя: здесь массивы всегда ссылочного типа. Покопавшись, все-таки нахожу что массив можно сделать fixed. Правда, из-за него одного весь проект приходится делать unsafe.
Начни с того, что ты хочешь сделать, а потом уже думай структуры.
7>Но главная проблема дальше. Данные принимаются в массив байт. В Си я создавал указатель типа CDataPacket, указывал им на начало массива и работал с ним. Здесь указателей нет, данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно? Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных? Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Ты можешь привести пример кода, иллюстрирующего проблему?
В C# указатели есть и можно делать ровно то же, что и на голом C. Естественно это все unsafe.
[StructLayout(LayoutKind.Explicit)] //чтобы руками задать layoutstruct DataPacket
{
[FieldOffset(0)]
public byte DevType; // тип устройства
[FieldOffset(1)]
public byte InfType; // тип информации
[FieldOffset(2)]
public byte Length; // длина
[FieldOffset(3)]
public fixed byte[255] Data; // данные
};
byte[] ReadPacket() { /*some code */}
var buf = ReadPacket();
fixed( byte* p = buf )
{
var s = (DataPacket*)p;
if(s->InfType == 0x01)
{
var s1 = (SomeOtherStruct*)&s->Data[0];
//...
}
//...
}
Так вполне можно писать.
Но лучше сразу при парсинге получать нужные структуры.
Вряд ли тебе будут данные приходить целыми пакетами по 258 байт. Скорее тебе надо прочитать Length и потом читать ровно только байт, сколько Length. В этот момент у тебя еще не будет массива, чтобы привести его к структуре, но ты уже распарсишь нужные значения. Поэтому твой код чтения может сразу выплюнуть не массив байт, а структуру нужного типа.
Здравствуйте, Философ, Вы писали:
Ф>Здравствуйте, Regular_Man, Вы писали:
R_M>>На самом деле все есть. Вот здесь, например, можно подробно почитать https://habrahabr.ru/post/114953/
Ф>Чувак там скромно умалчивает о том, что структуры в шарпе нельзя выделить в куче, и все эти fixed arrays лягут на стэке.
С чего ты взял? Marshal.AllocHGlobal
Ф>Да и есть в шарпе, например нет struct union — именно они наибольшую головную боль вызывают.
С чего ты взял? Есть StructLayout(LayoutKind.Explicit) и [FieldOffset(x)]
Ф>Я каждый раз, когда сталкиваюсь с Interop'ом задумываюсь о том, чтобы его писать на C++ CLI.
Интероп действительно проще на C++ делать, но это сразу дофига проблем дает — несколько языков в проекте, ограничения на платформы и много подобной "радости".
Если интеропа мало, то лучше один раз написать обертки на C# и как можно быстрее уйти от ковыряния с байтами и указателями.
Если весь проект это большой интероп, то лучше на C++ CLI, он для такого и был сделан.
Ф>Из-за вот такого, и из-за списков строк.
Какого еще списка строк?
Здравствуйте, 777777w, Вы писали:
7>Но главная проблема дальше. Данные принимаются в массив байт. В Си я создавал указатель типа CDataPacket, указывал им на начало массива и работал с ним. Здесь указателей нет, данные ссылочного типа создаются сразу со "своими" данными, а как быть если данные уже существуют отдельно? Ведь ситуация очень распространенная, двоичные файлы тоже читаются в массив байт, как их интерпретировать как структуры неких дынных? Дальше при обработке этой структуры тоже придется преобразовывать типы — поле Data в зависимости от типа устройства и типа информации интерпретируется как другие структуры, как здесь быть?
Закрепляешь управляемый массив в памяти, берёшь указатель на него (предварительно проверив, что длина массива больше 0), приводишь его к нужному тебе типу и работаешь с ним точно также, как в С++.
unsafe struct CDataPacket
{
public char DevType; // тип устройстваpublic byte InfType; // тип информацииpublic byte Length; // длинаpublic fixed byte Data [255]; // данные
};
unsafe static void ProcessPacket(byte[] packet)
{
if(packet == null || packet.Length == 0)
throw new ArgumentException("packet");
fixed(byte* ptr = packet)
{
CDataPacket* sPtr = (CDataPacket*)ptr;
String data = new String((sbyte*)sPtr->Data, 0, sPtr->Length);
Console.WriteLine(data);
}
}