代码之家  ›  专栏  ›  技术社区  ›  Matthew Layton

将非托管数据映射到.NET中的托管结构

  •  7
  • Matthew Layton  · 技术社区  · 11 年前

    我花了很多时间处理非托管代码和.NET中的平台调用。下面的代码说明了一些让我困惑的问题,即非托管数据是如何映射到.NET中的托管对象的。

    对于这个例子,我将使用 RECT 结构:

    C++RECT实现(非托管Win32 API)

    typedef struct _RECT {
      LONG left;
      LONG top;
      LONG right;
      LONG bottom;
    } RECT, *PRECT;
    

    C#RECT实现(托管.NET/C#)

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left, top, right, bottom;
    }
    

    好吧,那么我的C#等价物应该有效,对吧?我的意思是,所有的变量都与C++结构的顺序相同,并且使用相同的变量名。

    我的假设 LayoutKind.Sequential 意味着非托管数据映射到托管对象的顺序与它在C++结构中出现的顺序相同。即,数据将被映射,从左开始,然后是顶部,然后是右侧,然后是底部。

    在此基础上,我应该能够修改我的C#结构。。。

    C#RECT实现(稍微干净一点)

    [StructLayout(LayoutKind.Sequential)]
    public struct Rect //I've started by giving it a .NET compliant name
    {
        private int _left, _top, _right, _bottom; // variables are no longer directly accessible.
    
        /* I can now access the coordinates via properties */
        public Int32 Left
        {
            get { return _left; }
            set { this._left = value; }
        }
    
        public Int32 Top
        {
            get { return _top; }
            set { this._top = value; }
        }
    
        public Int32 Right
        {
            get { return _right; }
            set { this._right = value; }
        }
    
        public Int32 Bottom
        {
            get { return _bottom; }
            set { this._bottom = value; }
        }
    }
    

    那么,如果变量的声明顺序错误,会发生什么呢?大概这会把坐标搞砸,因为它们将不再映射到正确的东西上?

    public struct RECT
    {
        public int top, right, bottom, left;
    }
    

    据猜测,这张地图是这样的:

    顶部=左侧

    右侧=顶部

    底部=右侧

    left=底部

    所以我的问题很简单,我的假设是正确的吗?我可以根据每个变量的访问说明符,甚至变量名来修改托管结构,但我不能改变变量的顺序?

    4 回复  |  直到 11 年前
        1
  •  7
  •   Botz3000 Amir Sheng    11 年前

    如果您真的想更改成员变量的顺序,可以使用 FieldOffsetAttribute 。只是让它的可读性稍微降低了一点。

    您还需要设置 StructLayout LayoutKind.Explicit 表示您正在自己设置偏移。

    例子:

    [StructLayout(LayoutKind.Explicit)]
    public struct RECT
    {
        [FieldOffset(4)]  public int top;
        [FieldOffset(8)]  public int right;
        [FieldOffset(12)] public int bottom;
        [FieldOffset(0)]  public int left;
    }
    
        2
  •  2
  •   Jarek    11 年前

    是的,看来你的想法还可以。 StructLayout(LayoutKind.Sequential) 是应用于C的默认值# struct 所以你根本不需要这么做。但是,如果您希望有不同的字段顺序,可以使用 StructLayout(LayoutKind.Explicite) 然后申请 FieldOffset 属性到每个字段-这是一种更好的方法,因为你可以明确什么是隐含的,而不再依赖于可以轻易改变的东西,比如字段定义顺序。

    看看MSDN示例: StructLayoutAttribute Class 而且应该更清楚。此外,用C++和C#创建示例应用程序,并使用它来掌握它的窍门,这将使您受益匪浅。

        3
  •  1
  •   John Willemse    11 年前

    的默认映射 struct 在C#中是 LayoutKind.Sequential 。这防止编译器通过重新排列变量来优化内存,并确保正确的映射。

    然而,您可以通过使用告诉编译器变量的不同顺序 LayoutKind.Explicit FieldOffsetAttribute :

    [StructLayout(LayoutKind.Explicit)]
    public struct Rect
    {
        [FieldOffset(8)]
        public int right;
    
        [FieldOffset(4)]
        public int top;
    
        [FieldOffset(0)]
        public int left;
    
        [FieldOffset(12)]
        public int bottom;
    }
    

    的值 字段偏移属性 指示变量开头的结构中的字节位置。

        4
  •  0
  •   David Heffernan    11 年前

    我的假设是正确的吗?我可以根据每个变量的访问说明符,甚至变量名来修改托管结构,但我不能改变变量的顺序?

    是的,这是正确的。访问说明符和变量名都不会对结构的布局方式产生任何影响。