C#: Перебор объектов-членов вложенных структур

#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:

Тем временем я выяснил, что я сделал не так:

  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;   
  1. В функции «Инициализировать смещения» «field_info.GetValue (obj)» доставляет копию моего объекта-члена. Вот почему я должен скопировать измененную копию обратно в самом конце цикла foreach:
 field_info.SetValue (obj, node_obj);  

После этих изменений код работает так, как задумано. Спасибо за проявленный интерес. -Альберт