#c# #struct #reflection #iteration #packed
Вопрос:
Привет всем вам, волшебники c#!
Мне нужно сохранить все значения смещения памяти (упакованных) вложенных структур в этих соответствующих структурах. Повторный перебор всех элементов пока работает нормально. Кроме того, я получаю соответствующие значения смещения памяти. Это структурное устройство может содержать несколько десятков структур и, в конце концов, несколько сотен других элементов. Но я делаю все это во время инициализации, так что производительность процессора здесь не будет проблемой.
Но:
В этом итерационном процессе, похоже, мне трудно получить доступ к фактическим экземплярам этих структур. Как выясняется, когда я пытаюсь сохранить эти значения смещения, они оказываются не там, где мне нужно (конечно, они нужны мне в экземпляре «SomeStruct1» и в его экземплярах, содержащих другие экземпляры структуры, но отладчик четко показывает мне начальные значения (-1)).
Я подозреваю «field_info.GetValue» или «obj_type.InvokeMember» — это не то, что нужно для получения ссылки на объект? Есть ли какой — либо другой способ перебирать экземпляры вложенных структур?
Пожалуйста, помогите! Я отчаянно отлаживал и гуглил в течение трех дней, но сейчас у меня совсем нет идей…
Спасибо за ваши усилия!
-Альберт
PS — причина, по которой я делаю эти необычные вещи: я общаюсь между двумя встроенными ядрами процессора через упомянутую вложенную структуру (оба являются смешанными проектами на c/c ). Это работает как заклинание, так как оба ядра используют одну и ту же память, в которой находится структура.
Кроме того, мне нужно взаимодействовать между хост-приложением на c# и этими встроенными ядрами, поэтому я подумал, что было бы неплохо, если бы я реализовал третий экземпляр этой структуры. Только на этот раз я явно не могу использовать общую оперативную память. Вместо этого я внедряю задатчики и получатели значений для элементов хранения данных, определяю смещение памяти, а также длину элементов хранения данных и передаю эту информацию (вместе с самим значением) через USB или Ethernet во встроенную систему-поэтому «API» для моей встроенной системы будет просто структурой. Единственное обслуживание, которое я должен выполнять каждый раз, когда я изменяю структуру: я должен скопировать файл .h (встроенного проекта) в файл .cs (хост-проект). Я знаю, что это безумие, но сейчас это работает.
Спасибо за проявленный интерес. -Альберт
Это упрощенный (ошибочный, см. Ниже) пример, который следует скомпилировать и выполнить (WinForms, c#7.3):
using System; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; namespace CodingExample { public interface Interf { Int32 Offset {get; set; } } [StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct sSomeStruct2 : Interf { public sSomeStruct2 (bool dummy) { Offset = -1; SomeMember3 = 0; } public Int32 Offset {get; set; } public Int32 SomeMember3; // much more various-typed members (e. g. nested structs)... } [StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct sSomeStruct1 : Interf { public sSomeStruct1 (bool dummy) { Offset = -1; SomeMember1 = 0; SomeStruct2 = new sSomeStruct2 (true); SomeMember2 = 0; } public Int32 Offset {get; set; } public Int32 SomeMember1; public sSomeStruct2 SomeStruct2; public Int16 SomeMember2; // much more various-typed members... } public partial class Form1 : Form { void InitializeOffsets (object obj) { Console.WriteLine ("obj: {0}", obj); Type obj_type = obj.GetType (); foreach (FieldInfo field_info in obj_type.GetFields ()) { string field_name = field_info.Name; Int32 offset = (Int32) Marshal.OffsetOf (obj_type, field_name); Type field_type = field_info.FieldType; bool is_leafe = field_type.IsPrimitive; // none of theses three options seem to give me the right reference: // object node_obj = field_info.GetValue (obj); // object node_obj = field_info.GetValue (null); object node_obj = obj_type.InvokeMember (field_name, BindingFlags.GetField, null, obj, null); Console.WriteLine ("field: {0}; field_type: {1}; is_leafe: {2}; offset: {3}", field_name, field_type, is_leafe, offset); if (! is_leafe) { // this writes not as expected: (node_obj as Interf).Offset = offset; InitializeOffsets (node_obj); } } } sSomeStruct1 SomeStruct1; public Form1 () { InitializeComponent (); SomeStruct1 = new sSomeStruct1 (true); InitializeOffsets (SomeStruct1); } } }
Комментарии:
1. Может быть, вам стоит описать, что вы хотите сделать. Для чего ты все это делаешь. Должно быть лучшее решение, C# никогда не касался перемещения байтов и подсчета смещений в структурах памяти.
2. Что вы подразумеваете под «правильной ссылкой» ? У вас есть структуры. Их загоняют в угол. Мимоходом
object
, бросаяInterf
кому … повсюду создаются и отбрасываются новые ссылки. ОригиналSomeStruct1
, к которому вы переходитеInitializeOffsets
, копируется; на оригинал все это не влияет. Вместо этого используйте классы.3. @nvoigt: Я согласен, должно быть лучшее решение. Хорошо, я добавлю описание, почему я это делаю.
4. @madreflection: вы правы. Мне трудно избегать копий. К сожалению, мне нужны структуры.
Ответ №1:
Тем временем я выяснил, что я сделал не так:
- мне нужно заниматься боксом, поэтому я могу использовать «ref» при вызове функции инициализации:
// instead of this: SomeStruct1 = new sSomeStruct1 (true); // i have to do it this way: object boxed_SomeStruct1 = new sSomeStruct1 (true); InitializeOffsets (ref boxed_SomeStruct1); SomeStruct1 = (sSomeStruct1) boxed_SomeStruct1;
- В функции «Инициализировать смещения» «field_info.GetValue (obj)» доставляет копию моего объекта-члена. Вот почему я должен скопировать измененную копию обратно в самом конце цикла foreach:
field_info.SetValue (obj, node_obj);
После этих изменений код работает так, как задумано. Спасибо за проявленный интерес. -Альберт