首先来看一段代码:

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. pureftpd的搭建

    1,安装purefptd软件 ftp(file transfer protocol) 文件传输协议 cd  /usr/local/src  wget https://download.pureftpd ...

  2. yum安裝的包如何保留到本地

    一, 很多时候,我们一直用yum安装的软件,但是毫无疑问,很多人都会想yum安装的软件的包存放在哪里了呢? 这是因为yum默认并不保存你所安装的包,那么如何才能保留安装的软件包呢? 方法很简单:修改y ...

  3. Repeater动态添加行

    <table class="table table-striped table-bordered table-hover">            <asp:Re ...

  4. android一些常用的代码1(收藏)

    以下内容来自多个开源项目的整理和自己的项目积累. 1.拨打电话 public static void call(Context context, String phoneNumber) { conte ...

  5. IOS创建单例的两种方法

    1.0  苹果官方写法:  static AccountManager *DefaultManager = nil; + (AccountManager *)defaultManager { if ( ...

  6. HNOI2004 宠物收养所 解题报告

    首先读完这题第一印象,是个裸题,很高兴.其次在打完代码之后,第二印象,很恶心,Treap的代码太长了,我今天下午敲了三遍,手都麻了. 废话不多说,正题.其实这个题不难,有几个点是很好的,首先,他的a值 ...

  7. CATCell <——>CATPoint

    假定原先有CATCell tCell; CATVertex_var spVertex = tCell; CATPoint_var spPoint = spVertex -> GetPoint() ...

  8. 单光纤udp通信

    环境:      两块板子,拥有独立系统(Linux),通过单光纤连接(数据只能单向发送,无反馈).两块板子采用udp协议通信. 问题: 发送板子发送数据后,接收板子上的进程收不到数据. 确认两块光纤 ...

  9. 设置JQuery的Ajax方法同步

    Ajax请求默认的都是异步的如果想同步 async设置为false就可以(默认是true) var html = $.ajax({  url: "some.php",  async ...

  10. mysql插入返回当前生成的主键

     1:sql中需要添加属性 keyColumn="base_price_id" keyProperty="basePriceId" useGeneratedKe ...