-2

大家好,你们所有的 c# 向导!

我需要将(打包的)嵌套结构的所有内存偏移值存储在这些各自的结构中。 到目前为止,循环遍历所有成员都可以正常工作。另外,我得到了适当的内存偏移值。这个结构装置可能包含几十个结构,最后还有几百个其他成员。但是我在初始化时做这一切,所以 CPU 性能在这里不会成为问题。


但:

在这个迭代过程中,我似乎无法访问这些结构的实际实例。事实证明,当我尝试存储这些偏移值时,它们最终不会出现在我需要它们的位置(当然,我在实例“SomeStruct1”及其包含其他结构实例中需要它们,但调试器清楚地显示了我初始值 (-1))。

我怀疑“field_info.GetValue”或“obj_type.InvokeMember”不是获取对象引用的正确方法?有没有其他方法可以遍历嵌套的结构实例

请帮忙!我已经拼命调试和谷歌搜索了三天,但我现在没有想法......

感谢您的努力!

-阿尔伯特


PS - 我做这些不寻常的事情的原因:我通过提到的嵌套结构在两个嵌入式 CPU 内核之间进行通信(两者都是混合的 c/c++ 项目)。这就像一个魅力,因为两个核心共享相同的内存,结构所在的位置。

此外,我必须在 ac# 主机应用程序和这些嵌入式内核之间进行通信,所以我认为如果我实现这个结构的第三个实例,这可能是一件好事。只有这一次,我显然不能使用共享 RAM。相反,我为数据保存成员实现值设置器和获取器,找出内存偏移量以及数据保存成员的长度,并通过 USB 或以太网将此信息(连同值本身)提供给嵌入式系统 - 所以我的嵌入式系统的“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);
        }
    }
}
4

1 回答 1

0

同时我发现,我做错了什么:

  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. 在“InitializeOffsets”函数中,“field_info.GetValue (obj)”提供了我的成员对象的副本。这就是为什么我必须在 foreach 循环的最后将修改后的副本复制回来:
field_info.SetValue (obj, node_obj);

在这些更改之后,代码按预期工作。感谢您的关注。-阿尔伯特

于 2021-11-17T18:43:32.317 回答