记得一年以前在写代码的时候问了小伙伴一个问题,我说:“你说若是我在声明的时候初始化,它会在对象初始化的哪一个阶段被赋值?” 小伙伴想了想回答我:“仍是在构造函数里面给变量初始化吧。” 程序当口,时间比较紧,没有空去验证一下这个问题,后来也就慢慢淡忘了。设计模式
这个假期翻开“C#本质论”正好里面也提到了这种状况,虽然只是一笔带过,可是也勾起了个人回忆,那今天就来记录一下这个疑问以及它延伸出来的一些知识点。多线程
一、普通构造函数函数
public class ConstructionTest { int intPro = 1; public ConstructionTest() { } }
当一个实例字段在声明时赋值,其实编译器在处理的时候声明时赋值的位置被移动了,成为构造函数中的第一个语句。代码相似于如下的代码(若是有兴趣能够编译后查看IL):性能
public class ConstructionTest { int intPro; public ConstructionTest() { intPro = 1; } }
那么若是变量即在声明时进行了赋值,又在构造函数中赋值,那会是怎样的状况呢?答案就是:构造函数中的赋值会覆盖声明时初始化的值。代码相似于以下的代码:spa
public class ConstructionTest { int intPro; public ConstructionTest(int value) { intPro = 1;//声明时赋值 intPro = value;//构造函数中赋值 } }
因此,最好是在构造函数中给字段进行初始化工做,尤为是在团队协做开发的时候,你们都遵循相同的开发规范和准则对于复杂的项目来讲很是重要。.net
二、静态构造函数线程
静态构造函数每一本C#的相关数据都会阐述一边其特色,好比:每一个类只能有一个,声明时没有可见性的修饰符,必须用static进行修饰,而且不容许有参数。固然,相似普通的构造函数,它在构造函数内的初始化会覆盖其声明时初始化的内容。设计
那么在这里要记录的一些知识点是:1)静态构造函数调用的特色:不是显示调用,而是‘运行时’在首次访问类时自动调用的,并且只会被调用一次。所谓‘首次访问’,多是调用一个普通构造函数,也多是访问类的一个静态方法或者字段。正是因为静态构造函数的这个特色,咱们能够用它来解决Singleton模式中多线程状况下建立实例的问题。具体代码会在下面进行展现。2)关于静态初始化的建议:(原文)“尽可能在声明的同时完成静态初始化。静态构造器是在首次访问类的任何成员以前执行的,不管该成员是静态字段或者是静态方法,或者是其余的构造函数。为了提供对这一行为的支持,编译器会自动插入一些负责检查的代码。这些代码会检查全部类型的静态成员和构造器,确保静态构造器可以先运行。可是,若是没有静态构造器,编译器就会将全部静态成员初始化为他们的默认值,并且不会添加任何静态构造器的检查。这样形成的结果就是,只要访问任何静态字段,就会进行静态成员的赋值和初始化。可是,并非说只有完成了赋值和初始化,才能调用静态方法和实例构造器。有的时候,静态成员进行初始化的代价可能比较高,采用上述的设计,除非要访问一个静态字段,不然不须要对其进行初始化。但初始化静态成员能够在必定程度上提高性能。”这个建议很中肯,C#本质论里最引人注目的就是这几段的所谓的“高级主题”。code
利用静态变量的特色,静态构造函数特色实现的Singleton模式:对象
public sealed class Singleton { private static readonly Singleton instance; private Singleton(){} static Singleton() { instance = new Singleton(); } public static Singleton Instance { get { return instance; } } }
Singleton模式应该是最基础也最耳熟能详的设计模式了,另外的一种加锁的实现方式也很简单,你们能够参考连接:https://msdn.microsoft.com/zh-cn/library/ff650316.aspx
构造函数到这里就说完了,那么接下来还有一点点,就是new运算符。不少人解释过new运算符对实例的建立过程,好比说'你必须知道的.net'里很是细致的解释了这个过程,没有看过的朋友建议博客园里搜一下看看,固然最好是买本书慢慢品味。
可是我看到了‘C#本质论’里面的描述以后,不由感叹写得透彻。虽然仍是没有那么‘深刻’(没有列出IL),可是轻描淡写之间清晰的勾画出来new运算符和构造器之间交互的过程:new运算符从内存管理器获取内存,而后调用制定的构造器,将初始化好的内存传给构造器。接着构造器链剩余的部分开始执行,
在构造器之间传递初始化好的内存。这些构造器都没有返回值(都返回void),构造器链上的执行结束后,new运算符返回内存饮用,它指向的内存处于初始化好的形式。