beforefieldinit释义(2)
首先来看一段代码:
using System;
namespace BeforeFieldInit
{
internal class Foo
{
Foo(){ Console.WriteLine("Foo 对象构造函数");}
public static string Field = GetString("初始化 Foo 静态成员变量!"); public static string GetString(string s){
Console.WriteLine(s);
return s;
}
} internal class FooStatic
{
static FooStatic(){ Console.WriteLine("FooStatic 类构造函数"); }
FooStatic(){ Console.WriteLine("FooStatic 对象构造函数"); } public static string Field = GetString("初始化 FooStatic 静态成员变量!");
public static string GetString(string s){
Console.WriteLine(s);
return s;
}
} class Program
{
static void Main(string[] args){
Console.WriteLine("Main 开始 ..."); Foo.GetString("手动调用 Foo.GetString() 方法!");
//string info = Foo.Field; FooStatic.GetString("手动调用 FooStatic.GetString() 方法!");
//string infoStatic = FooStatic.Field; Console.ReadLine();
}
}
}
Foo 和FooStatic 唯一的不同就是FooStatic 有静态的类构造函数。执行上面的代码,输出如下:
如果把被注释的读取静态字段Field的两行代码打开,再编译运行,输出:
对比上面的区别,FooStatic 始终是延迟装载的,也就是只有类被首次使用时,类对象才被构造,其静态成员以及静态构造函数才被初始化执行,而Foo 类对象的初始化则交给CLR 来决定。
如果用IL Dasm.exe对比两个类生成的中间代码,可以看到只有一处不同:FooStatic 比Foo 少了一个特性:beforefieldinit。
也就是说静态构造函数抑制了beforefieldinit 特性,而该特性会影响对调用该类的时机。
C# 里面的静态构造函数,也称为类型构造器,类型初始化器,它是私有的,就是在上图中的.cctor : void()。CLR保证一个静态构造函数在每个AppDomain中只执行一次,而且这种执行是线程安全的,所以在静态构造函数中非常适合于单例模式的初始化(初始化静态字段等同于在静态构造函数中初始化,但不完全相同,因为显式定义静态构造函数会抑制beforefieldinit标志。)。
JIT编译器在编译一个方法时,会查看代码中引用了哪些类型,任何一个类型定义了静态构造函数,JIT编译器都会检查针对当前AppDomain,是否执行了这个静态构造函数。如果类型构造去没有执行,JIT编译器就会在生成的本地代码中添加对静态构造函数的一个调用,否则就不会添加,因为类型已经初始化。同时CLR还保证在执行本地代码中生成的静态构造函代码的线程安全。
根据上面的描述,我们知道JIT 必须决定是否生成类型静态构造函数代码,还须决定何时调用它。具体在何时调用有两中方式:
precise:JIT编译器可以刚好在创建类型的第一个实例之前,或刚好在访问类的一个非继承的字段或成员之前生产这个调用。
beforefieldinit:JIT编译器可以在首次访问一个静态字段或者一个静态/实例方法之前,或者创建类型的第一个实例之前,随便找一个时间生成调用。具体调用时机由CLR决定,它只保证访问成员之前会执行静态构造函数,但可能会提前很早就执行。
CLI specification (ECMA 335) 在 8.9.5 节中提到:
1. If marked BeforeFieldInit then the type's initializer method is executed at, or sometime before, first access to any static field defined for that type
2. If not marked BeforeFieldInit then that type's initializer method is executed at (i.e., is triggered by):
• first access to any static or instance field of that type, or
• first invocation of any static, instance or virtual method of that type
简单点说就是beforefieldinit可能会提前调用一个类型的静态构造函数,而precise模式是非要等到用时才调用类型的静态构造函数,它是严格的延迟装载。
beforefieldinit 是首选的(如果没有自定义静态构造函数,默认就是这种方式),因为它使CLR能够自由选择调用静态构造函数的时机,而CLR会尽可能利用这一点来生成运行得更快的代码。比如说在一个循环中调用单例(且包含首次调用),beforefieldinit方式可以让CLR决定在循环之前就调用静态构造函数来优化,而precise模式则只会在循环体中来调用静态构造函数,并在之后的调用会检测静态构造函数是否已被执行的标志位,这样效率稍低一些。在前面使用静态Field的情况下,beforefieldinit 方式下CLR也认为提前执行静态构造函数是更好的选择。
C# 的单例实现,可以利用 precise 延迟调用这一点来延迟对单例对象的构造(饿汗模式),从而带来一丁点的优化,但是在绝大部分情况下这一丁点的优化作用并不大!
beforefieldinit释义(2)的更多相关文章
- beforefieldinit释义(3)
1.看下面的例子: public static class MyClass<T> { public static readonly DateTime Time = GetNow(); pr ...
- beforefieldinit释义
首先让我们认识什么是,当字段被标记为beforefieldinit类型时,该字段初始化可以发生在任何时候任何字段被引用之前.这句话听起了有点别扭,接下来让我们通过具体的例子介绍. /// <su ...
- 常见linux命令释义(第八天)—— Bash Shell 的操作环境
换了新公司,公司的领导很不错.自己感受比较多的地方是,自己的工作效率明显比以前高了.以前会对频繁变动的需求十分不耐烦,现在接到需求后会仔细的思考,进行整体构建.即使以后需求有变动,也能够比较轻易的在原 ...
- 常见linux命令释义(第三天)
今天晚上看鸟哥的私房菜,边学边写笔记. 在linux中压缩大多是.tar, .tar.gz , .tgz, /gz, .bz2等. .gz 是通过gzip压缩的文件. .bz2 是通过bzip2压缩的 ...
- 常见linux命令释义(第一天)
快到中午吃饭了,然后忽然想起来samba里面没有添加用户.于是乎,就玩弄起了samba. Samba三下五除二就安装好了,想想window里面不断的点击下一步,还要小心提防各种隐藏再角落里的绑定软件. ...
- Tomcat的目录结构、处理流程、主配置文件(server.xml)释义
参考资料: http://www.cnblogs.com/xdp-gacl/p/3744053.html http://grass51.blog.51cto.com/4356355/1123400 1 ...
- 使用Perl5获取有道词典释义
Get Word Definition from dict.youda.com via Perl Script 获取基本释义 Get Basic Definition http://dict.youd ...
- Web前端名词释义及原理
引言:看题目的时候,不要觉得这是一个很深奥的问题,Web前端这些东西很多就是叫的名字牛逼,其实原理很TM简单,也就那么回事. 一.javascript名词释义 1.啥是事件队列? 就是 弄一个数组,里 ...
- linux内核的冒险md来源释义# 14raid5非条块读
linux内核的冒险md来源释义# 14raid5非条块读 转载请注明出处:http://blog.csdn.net/liumangxiong 假设是非条块内读.那么就至少涉及到两个条块的读,这就须要 ...
随机推荐
- dojo.declare
参考:http://www.ibm.com/developerworks/cn/web/1203_xiejj_dojodeclare/
- (转)Java字符串应用之密码加密与验证
1.通过java.Security.MessageDigest的静态方法getInstance创建具有指定算法名称的信息摘要,参数为算法名,传入”MD5“则表示使用MD5算法 2.Message ...
- exist和not exist用法
参考:http://wenku.baidu.com/view/577f4d49cf84b9d528ea7a6f.html //这个讲的很详细 引用自:http://chenling1018.bl ...
- 开发网站相关知识html和javascript
1.html 布局 https://github.com/bramstein/jlayout/ http://welcome.totheinter.net/columnizer-jquery-plug ...
- 网络受限下,使用Nexus要解决的两个问题
在网络受限的情况下,使用nexus总会遇到这么两个问题,让你头疼. 我头疼过了,为了不让大家头疼,把解决方案放在这里,供大家参考. 问题一.背景: 由于网络原因,Nexus无法更新远程仓库的索引. ...
- fdisk -l 找不到分区怎么办?想办法找到隐藏分区。
centos6.6 装系统的时候,选取系统默认分区.装好后发现少了一大部分空间,通过fdisk -l 找不到分区,另外使用 parted -l 同样找不到消失的柱面. 如: [root@mysql]# ...
- codeforces 518A. Vitaly and Strings
A. Vitaly and Strings time limit per test 1 second memory limit per test 256 megabytes input standar ...
- 正式学习React (七) react-router 源码分析
学习react已经有10来天了,对于react redux react-redux 的使用流程和原理,也已经有一定的了解,在我上一篇的实战项目里,我用到了react-route,其实对它还只是 停留在 ...
- JSON基础学习
定义 JSON时轻量级的文本数据交换格式,独立于语言,比xml更小更快更易解析 JSON解析器和JSON库支持不同的编程语言 4个基本规则 1. 并列数据间用 逗号, 2. 映射用冒号表示 3. 并列 ...
- 实现Linux下的U盘(USB Mass Storage)驱动
如何实现Linux下的U盘(USB Mass Storage)驱动 版本:v0.7 How to Write Linux USB MSC (Mass Storage Class) Driver Cri ...