直奔主题。
我们知道类中存在两种构造器,即类型构造器和实例构造器。
实例对象需要初始化,就是我们常说的new以下,其实new做了很多事,比如计算对象需要占用的内存空间,分配内存,初始对象的值,有时还会导致GC清理内存的操作。用一句行业语就是实例构造函数就是来构造实例对象的构造函数。
和实例化对象道理一样,类型对象也需要被构建, 类型构造器就是来初始化类型对象的构造函数。
不过这二者有点小不同。
类型构造器:
首先自然是类型构造器那么就只能执行一次构建,并且构建它的对象应该限于任何对象的产生(这里说的是AppDomain中的对象),换句话说它就该被CLR来调用,为了避免程序员去调用类型构造函数,类型构造函数被限定为private,但是如果你自己去体验编码是一定会骂我说谎,说加了private后编译器提示说类型构造函数前不能添加任何的访问修饰符,其实是这样滴,这个关键字会被编译器在编译阶段添加,所以你懂得。
其次,类型构造器必须是静态的,其实我貌似犯了个错误,这一点好像应该放在第一点来说,因为类型构造器和类型字段一样道理,因为他的加载顺序应该在只要调用了该对象的任何实例之前就加载(这个我们后面在说关于系统调用的事)。
第三点,有我说第一点可以理解,一般情况下带参的构造函数如果需要由程序来调用至少不应该是private,到不说构造函数不能使private,如果你将一个函数设为了private至少需要在一个非private的构造函数来调用它,否则,嘿嘿,你的这种做法有“代码异味”, 因为你的那个private的构造器就是一段没用的垃圾。因为我们说了类型构造器是private的并有CLR来调用,那么你要是弄个带参的,CLR多半要疯了,他怎么知道该给你穿什么玩意啊。所以规定类型构造器必须是无参的。
实例构造器:
实例构造器,这个就不想多说了,这个应该很熟悉,只要不加static随你怎么玩儿,出来private的构造器需要被其他构造函数调用外,你自己就看着办吧,只要是需要的你就可以玩儿。
还是来看看IDasm给我们的IL代码吧:
C#:

IL:

做到这儿,我突然想到一件事,哈哈,因为我看到以前的一位前辈给我讲singleton时说的两种模式,懒汉式和饿汉式,当时最纠结就是为了怕多线程访问这个单例模式,所以需要做很多判断,我突然想到,要是哪天我遇到一个项目需要用单例模式,哈哈,我就给特殊的component来处理这个单利模式,怎么处理,我们就
把单例模式的构建写到类型构造函数中去,或者说我不单独设个组件来处理吧,就是写到当前这个类里也是一件很让人省心的事。不过这个值得考究,比较没验证过。
why? 我建议说下类型构造器的工作模式,首先声明不是绝对的正确,只是一个大致的理解。
首先,我们说了类型构造器的调用时CLR来完成的,CLR在调用一段代码时,这里我们暂且理解为函数吧,这个时候他会首先去检测这些代码引用了哪些类型。同时他会在AppDomain中检测类型是不是已经加载,如果没有加载那就回去加载这些类型,这个时候一旦我们定义了类型构造器,它就会来执行我们的类型构造器,不同的是,CLR为了避免对类型对象的破坏,对多线程的访问加了互斥线程同步锁,类型构造器的执行,在构造完类型对象后,在AppDomain中这个对象就会存在了,下一次再有对象需要这个类型是发现类型对象存在,CLR就不做调用类型构造器了,即使有多个多项调用这个类型构造器,但此时尚未构造完这个类型对象,我们上面说了会有锁机制来保证,一旦类型对象完成构建下面的对类型构建的操作就会直接返回。所以,这就是我投机取巧的立足点。
说道这里我突然想多嘴,我们知道Object有四个基本的方法,其中一个Gethashcode的方法,这个大概没多少人用过,其实这个和我们的类型构造没多少直接联系,说他是因为上面我说的了“CLR为了避免对类型对象的破坏 ”这一句,为什么这么说?原因是CLR认为把对象和hash结合有很多的好处,从内存的分配,从访问效率,从安全性(这个扯远了,呵呵)。我们发现实例每一个对象都有一个对应的hashcode值,我们做个试验:
----定义了一个空类
class hashCodeTest
{
}
-----实例化一些对象
hashCodeTest test; for (int i = 0; i < 10; i++)
{
test=new hashCodeTest();
Console.WriteLine(test.GetHashCode());
}
运行结果:
我们发现每次运行hashcode都不一样,这是理所当然的,因为new出了不同对象,hashcode也反映了对象内存的分布,记住不是对应的地址,是对地址的一个映射,响应的equals方法我们经常强调,如果改写了equals方法那么也一定要记住改写gethashcode。为什么呢?因为equals是对对象值进行比较。与之相对的==是对对象应用的比较。Object默认的gethashcode如果类型构造器允许多次执行,就意味着类型对象可能改变它原来的内存地址,这将是一件非常可怕的事情,因为先前引用他的类将指向一个不可预计的地址。当然这与hashcode本身毫无关系,至少我们从hashcode的值上看出来的端倪。注意区分内存的分配和hashcode与之相映射的关系,这个hashcode是对地址的一个反映,或者说按照一套hash算法得出地址对应的hashcode值,因此如果说CLR版本变了,得出的hash值也不同的,所以不要认为只要值是相同的那么hashcode也一定是一样的,也不要实例化hashcode的值来做判定处理等,举个例子用户的验证、所谓的加密处理,因为hashcode是不可以逆的。有兴趣可以做个试验看看,定义一个字符串,然后起这个字符串的hashcode,你会发现只有是值相等hashcode的值也相当,但是如果你换个CLR版本就不一定了。这也是我在上面说hashcode只是一个内存地址按照hash算法的隐射地址而不是真实地址的原因。
本文出自 “碧雪青天” 博客,请务必保留此出处http://oversky.blog.51cto.com/1026601/1381291
构造器,布布扣,bubuko.com
构造器
原文:http://oversky.blog.51cto.com/1026601/1381291