专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »DotNet » 地质构造类型:深入.NET的类型构造器 »正文

地质构造类型:深入.NET的类型构造器

来源: 发布时间:星期四, 2009年2月12日 浏览:108次 评论:0


1 引言
今天Artech兄在有关Type Initializer和 BeforeFieldInit问题看看大家能否给出正确解释文中让我们认识了个有关类型构造器执行有趣举例其中也相应提出了些有关beforefieldinit对于类型构造器时机探讨对于我们很好理解类型构造器给出了个很好应用实战体验
作为补充本文希望从基础开始再层层深入有关Type Initializer和 BeforeFieldInit问题看看大家能否给出正确解释文中没有解释概念和原理进行必要补充例如更全面认识类型构造器认识BeforeFieldInit并在此基础上探讨点有关类型构造器实战应用同时期望能够回答其中举例运行结果
废话少说我们开始

2 认识对象构造器和类型构造器

在.NET中个类化过程是在构造器中进行并且根据构造成员类型分为类型构造器(.cctor)和对象构造器(.ctor) 其中.cctor和.ctor为 2者在IL代码中指令表示.cctor不能被直接规则正是本文欲加阐述重点详见后文分析;而.ctor会在类型例子化时被自动
基于对类型构造器探讨我们有必要首先实现个简单类定义其中包括普通构造器和静态构造器例如

// Release : code01, 2008/11/02 // Author : Anytao, http://www.anytao.com public User { User { message = "Initialize in constructor."; } public User { message = "Initialize in normal construcotr."; } public User( name, age) { Name = name; Age = age; } public Name { get; ; } public Age { get; ; } public message = "Initialize when d.";我们将上述代码使用ILDasm.exe工具反编译为IL代码可以很方便找到相应类型构造器和对象构造器影子如图



然后我们简单来了解下对象构造器和类型构造器概念

对象构造器(.ctor)
在生成IL代码中将可以看到对应ctor类型例子化时会执行对应构造器进行类型操作
有关例子化过程设计到比较复杂执行顺序按照类型基础层次进行过程可以参阅你必须知道.NET7.8节 “动静的间:静态和非静态”文中有详细介绍和分析本文中将不做过多探讨
本文重点以考察类型构造器为主所以在此不进行过多探讨
类型构造器(.cctor)
用于执行对静态成员在.NET中类型在两种情况下会发生对.cctor:
为静态成员指定例如上例中只有静态成员而没有静态构造.cctorIL代码实现为:
.method private hidebysig specialname rtspecialname void .cctor cil managed{ // Code size 11 (0xb) .maxstack 8 IL_0000: ldstr "Initialize when d." IL_0005: stsfld Anytao.Write.TypeInit.User::message IL_000a: ret} // end of method User::.cctor实现显式静态构造例如上例中有静态构造存在时将首先执行静态成员化过程再执行静态构造化过程.cctorIL代码实现为:
.method private hidebysig specialname rtspecialname void .cctor cil managed{ // Code size 23 (0x17) .maxstack 8 IL_0000: ldstr "Initialize when d." IL_0005: stsfld Anytao.Write.TypeInit.User::message IL_000a: nop IL_000b: ldstr "Initialize in constructor." IL_0010: stsfld Anytao.Write.TypeInit.User::message IL_0015: nop IL_0016: ret} // end of method User::.cctor同时我们必须明确些静态构造基本规则包括:

必须为静态无参构造并且个类只能有
只能对静态成员进行
静态无参构造可以和非静态无参构造共存区别在于 2者执行时间详见你必须知道.NET7.8节 “动静的间:静态和非静态”论述其他更多区别和差异也详见本节描述
3 深入执行过程

类型构造器本身特点定程度上决定了.cctor时机并非是个确定概念类型构造器都是private用户不能显式类型构造器所以有关类型构造器执行时机问题在.NET中主要包括两种方案:

precise方式
beforefieldinit方式
2者执行差别主要体现在是否为类型实现了显式静态构造如果实现了显式静态构造则按照precise方式执行;如果没有实现显式静态构造则按照beforefieldinit方式执行
为了说清楚类型构造器执行情况我们首先在概念上必须明确个前提那就是precise语义明确了.cctor存取静态成员时机存在精确关系所以换句话说类型构造器执行时机在语义上决定于是否显式声明了静态构造以及存取静态成员时机这两个原因
我们还是从User类实现说起过招分析这两种方式执行过程
3.1 precise方式
首先实现显式静态构造方案为:

// Release : code02, 2008/11/02 // Author : Anytao, http://www.anytao.com public User { //Explicit Constructor User { message = "Initialize in constructor."; } public message = "Initialize when d."; }对应IL代码为:

. public auto ansi User extends [mscorlib].Object{ .method private hidebysig specialname rtspecialname void .cctor cil managed { .maxstack 8 L_0000: ldstr "Initialize when d." L_0005: stsfld Anytao.Write.TypeInit.User::message L_000a: nop L_000b: ldstr "Initialize in constructor." L_0010: stsfld Anytao.Write.TypeInit.User::message L_0015: nop L_0016: ret } .method public hidebysig specialname rtspecialname instance void .ctor cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void [mscorlib].Object::.ctor L_0006: ret } .field public message}为了进行对比分析我们需要首先分析beforefieldinit方式执行情况所以接着继续


3.2 beforefieldinit方式
为User类型不实现显式静态构造方案为:

// Release : code03, 2008/11/02 // Author : Anytao, http://www.anytao.com public User { //Implicit Constructor public message = "Initialize when d."; }对应IL代码为:

. public auto ansi beforefieldinit User extends [mscorlib].Object{ .method private hidebysig specialname rtspecialname void .cctor cil managed { .maxstack 8 L_0000: ldstr "Initialize when d." L_0005: stsfld Anytao.Write.TypeInit.User::message L_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void [mscorlib].Object::.ctor L_0006: ret } .field public message}3.3 分析差别
从IL代码执行过程而言我们首先可以了解是在显式和隐式实现类型构造内部除了添加新化操作的外 2者实现是基本相同所以要找出两种方式差别我们最终将着眼点锁定在 2者元数据声明上隐式方式多了个称为beforefieldinit标记指令
那么beforefieldinit究竟表示什么样语义呢?Scott Allen对此进行了详细解释:beforefieldinit为CLR提供了在任何时候执行.cctor授权只要该思路方法在第次访问类型静态字段的前执行即可
所以如果对precise方式和beforefieldinit方式进行比较时 2者差别就在于是否在元数据声明时标记了beforefieldinit指令precise方式下CLR必须在第次访问该类型静态成员或者例子成员的前执行类型构造器也就是说必须刚好在存取静态成员或者创建例子成员的前完成类型构造器;beforefieldinit方式下CLR可以在任何时候执行类型构造器定程度上实现了对执行性能优化因此较precise方式更加高效
值得注意当有多个beforefieldinit构造器存在时CLR无法保证这多个构造器的间执行顺序因此我们在实际编码时应该尽量避免这种情况发生

4 回归问题必要小结

本文源于Artech兄个问题希望通过上文分析可以给出点值得参考背景现在就有关Type Initializer和 BeforeFieldInit问题看看大家能否给出正确解释文中几个举例进行些继续分析:

在蒋兄开始举例实现中可以很容易来确定对于显式实现了静态构造情况类型构造器在刚好引用静态成员的前发生所以不管是否在Main中声明
field = Foo.Field;执行结果不受影响

而在没有显式实现静态构造情况下beforefieldinit优化了类型构造器执行不在确定时间执行只要实在静态成员引用或者类型例子发生的前即可所以在Debug环境下时机变得不按常理然而在Release优化模式下beforefieldinit执行顺序并不受
field = Foo.Field;影响完全符合beforefieldinit优化执行语义定义

有关最后个静态成员继承情况结果正像本文开始描述逻辑类型构造器是在静态成员被或者创建例子时发生所以举例结果是完全遵守规范标准不过我并不建议子类最好不要父类静态成员原因是作为继承机制而言子承父业是继承基本规范标准除了强制为private的外所有成员或者思路方法都应在子类中可见而对于存在潜在问题更好以规范标准来约束可能会更好其中静态思路方法定程度上是种结构化实现机制在面向对象继承关系中本质上就存在不足
在c#规范标准中有关beforefieldinit控制已经引起很多关注和非议方面beforefieldinit方式可以有效优化性能但是以显式和或者隐式实现静态构造方式不能更有直观开发者来控制因此在以后版本c#中能实现基于特性声明方式来控制是值得期待
方面在有两个类型类型构造器相互引用情况下CLR无法保证类型构造器顺序开发者而言我同样强调了对于类型构造器而言我们应该尽量避免要求顺序相关业务逻辑很多时候执行顺序并非声明顺序这是值得关注

0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: