7

我花了很多时间处理非托管代码和 .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;
}

猜测一下,这将像这样映射:

上=左

右=上

底部=右

左=底部

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

4

4 回答 4

7

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

您还需要设置 yourStructLayoutLayoutKind.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;
}
于 2013-04-15T10:31:57.677 回答
2

是的,看来你的想法还可以。StructLayout(LayoutKind.Sequential)是应用于 C# 的默认值,struct因此您甚至不需要这样做。但是,如果您想拥有不同的字段顺序,您可以通过使用StructLayout(LayoutKind.Explicite)然后将FieldOffset属性应用于每个字段来做到这一点 - 这是更好的方法,因为您明确了隐含的内容,而不再依赖于可以轻松更改的内容像字段定义顺序。

看看 MSDN 示例:StructLayoutAttribute Class它应该更清楚。另外 - 在 C++ 和 C# 中创建示例应用程序并使用它来掌握它 - 这将使您受益匪浅。

于 2013-04-15T10:34:55.223 回答
1

structC#中 a 的默认映射是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;
}

的值FieldOffsetAttribute指示变量开始的结构中的字节位置。

于 2013-04-15T10:36:46.553 回答
0

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

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

于 2013-04-15T19:11:27.820 回答