首先来看一段代码:

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)的更多相关文章

  1. beforefieldinit释义(3)

    1.看下面的例子: public static class MyClass<T> { public static readonly DateTime Time = GetNow(); pr ...

  2. beforefieldinit释义

    首先让我们认识什么是,当字段被标记为beforefieldinit类型时,该字段初始化可以发生在任何时候任何字段被引用之前.这句话听起了有点别扭,接下来让我们通过具体的例子介绍. /// <su ...

  3. 常见linux命令释义(第八天)—— Bash Shell 的操作环境

    换了新公司,公司的领导很不错.自己感受比较多的地方是,自己的工作效率明显比以前高了.以前会对频繁变动的需求十分不耐烦,现在接到需求后会仔细的思考,进行整体构建.即使以后需求有变动,也能够比较轻易的在原 ...

  4. 常见linux命令释义(第三天)

    今天晚上看鸟哥的私房菜,边学边写笔记. 在linux中压缩大多是.tar, .tar.gz , .tgz, /gz, .bz2等. .gz 是通过gzip压缩的文件. .bz2 是通过bzip2压缩的 ...

  5. 常见linux命令释义(第一天)

    快到中午吃饭了,然后忽然想起来samba里面没有添加用户.于是乎,就玩弄起了samba. Samba三下五除二就安装好了,想想window里面不断的点击下一步,还要小心提防各种隐藏再角落里的绑定软件. ...

  6. Tomcat的目录结构、处理流程、主配置文件(server.xml)释义

    参考资料: http://www.cnblogs.com/xdp-gacl/p/3744053.html http://grass51.blog.51cto.com/4356355/1123400 1 ...

  7. 使用Perl5获取有道词典释义

    Get Word Definition from dict.youda.com via Perl Script 获取基本释义 Get Basic Definition http://dict.youd ...

  8. Web前端名词释义及原理

    引言:看题目的时候,不要觉得这是一个很深奥的问题,Web前端这些东西很多就是叫的名字牛逼,其实原理很TM简单,也就那么回事. 一.javascript名词释义 1.啥是事件队列? 就是 弄一个数组,里 ...

  9. linux内核的冒险md来源释义# 14raid5非条块读

    linux内核的冒险md来源释义# 14raid5非条块读 转载请注明出处:http://blog.csdn.net/liumangxiong 假设是非条块内读.那么就至少涉及到两个条块的读,这就须要 ...

随机推荐

  1. What should we do when meet a crash in android?

    制造一个crash   为了演示的目的,我在libsensors的open_sensors_device中故意制造了一个crash:   static int open_sensors_device( ...

  2. 自学HTML的几个例子

    此处不赘述HTML中不同标签的用法仅仅给出自己学习时写的一些自娱自乐的例子,具体标签用法请参考W3C(http://www.w3school.com.cn/),毕竟这个才是最靠谱的,请不要相信任何二道 ...

  3. 基于.Net的单点登录(SSO)解决方案

    前些天一位朋友要我帮忙做一单点登录,其实这个概念早已耳熟能详,但实际应用很少,难得最近轻闲,于是决定通过本文来详细描述一个SSO解决方案,希望对大家有所帮助.SSO的解决方案很多,但搜索结果令人大失所 ...

  4. Sql Server 2012启动存储过程

    可以通过如下步骤创建 1.打开show advanced options reconfigure 2.打开scan for startup procs,使得sql server在启动时扫描需要运行的p ...

  5. silverlight调用bing地图 和 显示中文地图

    bing地图sdk: https://msdn.microsoft.com/en-us/library/ff428643.aspx 引用dll:https://www.microsoft.com/en ...

  6. C# 与MySQL

    1. MySQL.Data.dll       http://files.cnblogs.com/files/lwngreat/MySql.Data.rar 2.在工程中添加引用 3. 使用  Mys ...

  7. 记微信开发(有道翻译api)

    记微信开发(有道翻译api) 记微信开发(有道翻译api) 效果: 有道翻译api申请: 地址:http://fanyi.youdao.com/openapi code: <?php/** * ...

  8. Activiti+oracle 启动项目时不能自动建表或更新表的问题分析及解决办法

    现象描述:按照正常配置,第一次启动时不能自动建表 关键配置片段如下: <bean id="processEngineConfiguration" class="or ...

  9. MSSQL 获取数据库字段类型

    SELECT col.name AS 列名, typ.name as 数据类型, col.max_length AS 占用字节数, col.precision AS 数字长度, col.scale A ...

  10. Apache无法启动解决方案

    Apache无法启动错误原因: 原因一:80端口占用例如IIS,另外就是迅雷.我的apache服务器就是被迅雷害得无法启用! 原因二:软件冲突装了某些软件会使apache无法启动如Dr.com 你打开 ...