博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CLR笔记 三 类型中字段布局
阅读量:6914 次
发布时间:2019-06-27

本文共 4687 字,大约阅读时间需要 15 分钟。

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

你可能感兴趣的文章
《量化金融R语言初级教程》一2.3 使用真实数据
查看>>
《C++编程惯用法——高级程序员常用方法和技巧》——1.4 使用CRC卡片来辅助设计...
查看>>
《Adobe Flash CS6中文版经典教程》——1.10 修改内容和舞台
查看>>
《Python高手之路》——2.7 Christophe de Vienne访谈
查看>>
《iOS 6高级开发手册(第4版)》——1.10节秘诀:Core Motion基础
查看>>
《Ansible权威指南》一1.6 Ansible应用场景
查看>>
HTML超链接
查看>>
Netty知识笔记
查看>>
wampserver如何配置。
查看>>
架构的演进,阿里资深Java工程师表述架构的腐化之谜
查看>>
android中调用native的fork函数后的现象观察
查看>>
javascript模拟new的实现
查看>>
IDEA快捷键
查看>>
微信小程序模板介绍
查看>>
JDK源码阅读-Iterable接口
查看>>
【算法刷题】2:寻找两个有序数组的中位数
查看>>
Linux学习笔记(三)-Linux 磁盘与文件系统管理
查看>>
站在巨人的肩膀上
查看>>
阿里巴巴fastjson @JSONField 注解说明
查看>>
Android开发 - 解决DialogFragment在全屏时View被状态栏遮住的问题
查看>>