CLR笔记 三 类型中字段布局

CLR如何控制类型中的字段布局

为提高性能,在未指定类型或者结构体的字段的任何排列方式时,CLR会按照自己认为最合适的方式进行重新排列,即内存对齐。

如何指定类或者结构体内字段的排列方式

在类型或者结构体内添加特性System.Runtime.InteropServices.StructLayoutAttribute;

传递枚举参数LayoutKind

具体参数说明(微软代码有介绍):

 1     //
 2     // 摘要:
 3     //     当将对象导出到非托管代码时,控制该对象的布局。
 4     [ComVisible(true)]
 5     public enum LayoutKind
 6     {
 7         //
 8         // 摘要:
 9         //     对象的成员顺序依次布局,它们出现导出到非托管内存时的顺序。 成员进行布局中指定的封装根据System.Runtime.InteropServices.StructLayoutAttribute.Pack,和可以是不连续。
10         Sequential = 0,
11         //
12         // 摘要:
13         //     非托管内存中的每个成员的对象的精确位置显式控制,受约束的设置System.Runtime.InteropServices.StructLayoutAttribute.Pack字段。
14         //     每个成员必须使用System.Runtime.InteropServices.FieldOffsetAttribute指示该字段的类型中的位置。
15         Explicit = 2,
16         //
17         // 摘要:
18         //     非托管内存中某个对象的成员,则运行时会自动选择适当的布局。 无法在托管代码之外公开使用此枚举成员定义的对象。 尝试这样做将引发异常。
19         Auto = 3
20     }
21 }
View Code

需要注意的是Microsof C#编译器总是默认的为引用类型选择Layoutkind.Auto,为值类型选择LayoutKind.Sequential(也许其他的C#编译器不是这么做的)。

LayoutKind.Sequential一般用于和非托管代码进行交互,若不与非托管代码进行交互可以显式指定为Auto可以提高性能。

LayoutKind.Explicit:精确指定字段偏移量(相对于实例的起始位置),使用FieldOffSet特性指定。

Tips:

对于LayoutKind.Explicit:

1.一般用于C/C++具有union的类,该类的数据成员在内存中的存储互相重叠,每个数据成员都冲相同的内存地址开始,分配给union的存储区数量是它包含最大数据成员所需的内存数,同一时刻只有一个成员可以被赋值。

2.引用类型和值类型互相重叠是不合法的,多个引用类型互相重叠时,该类型无法验证,多个值类型互相重叠是合法的,但所有重叠字节都能通过公共字段访问,方能够验证;

实际应用

通过win32函数获取当前操作系统的逻辑CPU核心数:GetSystemInfo或者GetNativeSystemInfo(微软API:https://msdn.microsoft.com/en-us/library/ms724381(v=VS.85).aspx),其输出类型结构体为:SYSTEM_INFO:

该结构体定义:

typedef struct _SYSTEM_INFO {
  union {
    DWORD  dwOemId;
    struct {
      WORD wProcessorArchitecture;
      WORD wReserved;
    };
  };
  DWORD     dwPageSize;
  LPVOID    lpMinimumApplicationAddress;
  LPVOID    lpMaximumApplicationAddress;
  DWORD_PTR dwActiveProcessorMask;
  DWORD     dwNumberOfProcessors;
  DWORD     dwProcessorType;
  DWORD     dwAllocationGranularity;
  WORD      wProcessorLevel;
  WORD      wProcessorRevision;
} SYSTEM_INFO;

其中含有一个匿名联合体体,该联体内字段dwOemid与匿名联合体中的匿名结构体起始地址一样,由于dwOemid是DWORD类,而匿名结构体内的两个字段都为WORD类型,DWORD类型是4个字节类型,WORD是双字节,所以dwOemId的低16位与wProcessArchitecture内存共用,dwOemId的高16位与wReserved内存共用;所以在C#代码中可以这样声明SYSTEM_INFO结构体:

 1         [StructLayout(LayoutKind.Sequential)]
 2         public struct System_Info
 3         {
 4             /// <summary>
 5             /// 已过时为保持兼容性留下的结构体
 6             /// </summary>
 7             [Obsolete("已过时为保持兼容性留下的结构体")]
 8             public OemId oemId;
 9             /// <summary>
10             /// 
11             /// </summary>
12             public UInt32 dwPageSize;
13             /// <summary>
14             /// 应用程序或者DLL最小地址指针
15             /// </summary>
16             public IntPtr lpMinimumApplicationAddress;
17             /// <summary>
18             /// 应用程序或者DLL最大地址指针
19             /// </summary>
20             public IntPtr lpMaximumApplicationAddress;
21             /// <summary>
22             /// 系统内处理器配置的标记
23             /// </summary>
24             public IntPtr dwActiveProcessorMask;
25             /// <summary>
26             /// 处理器数量
27             /// </summary>
28             public UInt32 dwNumberOfProcessors;
29             /// <summary>
30             /// 处理器类型
31             /// </summary>
32             [Obsolete("已过时,为兼容性保留,")]
33             public UInt32 dwProcessorType;
34             /// <summary>
35             /// 虚拟内存分配的颗粒度
36             /// </summary>
37             public UInt32 dwAllocationGranularity;
38             /// <summary>
39             /// 架构相关的处理器等级
40             /// </summary>
41             public UInt16 wProcessorLevel;
42             /// <summary>
43             /// 处理器修订号
44             /// </summary>
45             public UInt16 wProcessorRevision;
46         }
47 
48         /// <summary>
49         /// 已过时为保持兼容性留下的结构体
50         /// </summary>
51         [StructLayout(LayoutKind.Explicit)]
52         public struct OemId
53         {
54             [FieldOffset(0)]
55             UInt32 dwOemId;
56             /// <summary>
57             /// 安装系统处理器架构
58             /// </summary>
59             [FieldOffset(0)]
60             UInt16 wProcessArchitecture;
61             /// <summary>
62             /// 将来预留字段
63             /// </summary>
64             [FieldOffset(16)]
65             UInt16 wReserved;
66         }
67 
68         public enum ProcessArchitecture
69         {
70             /// <summary>
71             /// x64 AMD or intel
72             /// </summary>
73             PROCESSOR_ARCHITECTURE_AMD64 = 9,
74             /// <summary>
75             /// ARM
76             /// </summary>
77             PROCESSOR_ARCHITECTURE_ARM = 5,
78             /// <summary>
79             /// ARM64
80             /// </summary>
81             PROCESSOR_ARCHITECTURE_ARM64 = 12,
82             PROCESSOR_ARCHITECTURE_IA64 = 6,
83             PROCESSOR_ARCHITECTURE_INTEL = 0,
84             PROCESSOR_ARCHITECTURE_UNKNOWN = 0xffff,
85         }
View Code

然后调用下看看结果。

 

问题

1.LayoutKind.Auto为什么可以提高性能?

https://www.cnblogs.com/kex1n/archive/2009/06/16/2286527.html

2.什么是类型验证?如何进行类型验证?

 

参考:

1.CLR Via C#;2.

转载于:https://www.cnblogs.com/forevertime/p/9246262.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值