.Net的类型构造器-static构造函数
品味細(xì)節(jié),深入.NET的類型構(gòu)造器
轉(zhuǎn)載自:http://msdn.microsoft.com/zh-cn/dd368012.aspx
| ? |
1 引言
今天Artech 兄在《關(guān)于 Type Initializer和 BeforeFieldInit的問(wèn)題,看看大家能否給出正確的解釋 》一文中讓我們認(rèn)識(shí)了一個(gè)關(guān)于類型構(gòu)造器調(diào)用執(zhí)行的有趣示例,其中也相應(yīng)提出了一些關(guān)于beforefieldinit 對(duì)于類型構(gòu)造器調(diào)用時(shí)機(jī)的探討,對(duì)于我們很好的理解類型構(gòu)造器給出了一個(gè)很好的應(yīng)用實(shí)踐體驗(yàn)。
作為補(bǔ)充,本文希望從基礎(chǔ)開(kāi)始再層層深入,把《關(guān)于Type Initializer 和 BeforeFieldInit 的問(wèn)題,看看大家能否給出正確的解釋》一文中沒(méi)有解釋的概念和原理,進(jìn)行必要的補(bǔ)充,例如更全面的認(rèn)識(shí)類型構(gòu)造器,認(rèn)識(shí)BeforeFieldInit 。并在此基礎(chǔ)上,探討一點(diǎn)關(guān)于類型構(gòu)造器的實(shí)踐應(yīng)用,同時(shí)期望能夠回答其中示例運(yùn)行的結(jié)果。
廢話少說(shuō),我們開(kāi)始。
2 認(rèn)識(shí)對(duì)象構(gòu)造器和類型構(gòu)造器
在.NET 中,一個(gè)類的初始化過(guò)程是在構(gòu)造器中進(jìn)行的。并且根據(jù)構(gòu)造成員的類型,分為類型構(gòu)造器(.cctor )和對(duì)象構(gòu)造器(.ctor ), 其中.cctor 和.ctor 為二者在IL 代碼中的指令表示。.cctor 不能被直接調(diào)用,其調(diào)用規(guī)則正是本文欲加闡述的重點(diǎn),詳見(jiàn)后文的分析;而.ctor 會(huì)在類型實(shí)例化時(shí)被自動(dòng)調(diào)用。
基于對(duì)類型構(gòu)造器的探討,我們有必要首先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的類定義,其中包括普通的構(gòu)造器和靜態(tài)構(gòu)造器,例如
我們將上述代碼使用ILDasm.exe 工具反編譯為IL 代碼,可以很方便的找到相應(yīng)的類型構(gòu)造器和對(duì)象構(gòu)造器的影子,如圖
然后,我們簡(jiǎn)單的來(lái)了解一下對(duì)象構(gòu)造器和類型構(gòu)造器的概念。
- 對(duì)象構(gòu)造器(.ctor )
在生成的IL 代碼中將可以看到對(duì)應(yīng)的ctor ,類型實(shí)例化時(shí)會(huì)執(zhí)行對(duì)應(yīng)的構(gòu)造器進(jìn)行類型初始化的操作。
- 類型構(gòu)造器(.cctor )
用于執(zhí)行對(duì)靜態(tài)成員的初始化,在.NET 中,類型在兩種情況下會(huì)發(fā)生對(duì).cctor 的調(diào)用:
1. 為靜態(tài)成員指定初始值,例如上例中只有靜態(tài)成員初始化,而沒(méi)有靜態(tài)構(gòu)造函數(shù)時(shí),.cctor 的IL 代碼實(shí)現(xiàn)為:
2. 實(shí)現(xiàn)顯式的靜態(tài)構(gòu)造函數(shù),例如上例中有靜態(tài)構(gòu)造函數(shù)存在時(shí),將首先執(zhí)行靜態(tài)成員的初始化過(guò)程,再執(zhí)行靜態(tài)構(gòu)造函數(shù)初始化過(guò)程,.cctor 的IL 代碼實(shí)現(xiàn)為:
同時(shí),我們必須明確一些靜態(tài)構(gòu)造函數(shù)的基本規(guī)則,包括:
- 必須為靜態(tài)無(wú)參構(gòu)造函數(shù),并且一個(gè)類只能有一個(gè)。
- 只能對(duì)靜態(tài)成員進(jìn)行初始化。
- 靜態(tài)無(wú)參構(gòu)造函數(shù)可以和非靜態(tài)無(wú)參構(gòu)造函數(shù)共存,區(qū)別在于二者的執(zhí)行時(shí)間,詳見(jiàn)《你必須知道的.NET 》7.8 節(jié) “ 動(dòng)靜之間:靜態(tài)和非靜態(tài)” 的論述,其他更多的區(qū)別和差異也詳見(jiàn)本節(jié)的描述。
3 深入執(zhí)行過(guò)程
因?yàn)轭愋蜆?gòu)造器本身的特點(diǎn),在一定程度上決定了.cctor 的調(diào)用時(shí)機(jī)并非是一個(gè)確定的概念。因?yàn)轭愋蜆?gòu)造器都是private 的,用戶不能顯式調(diào)用類型構(gòu)造器。所以關(guān)于類型構(gòu)造器的執(zhí)行時(shí)機(jī)問(wèn)題在.NET 中主要包括兩種方案:
- precise 方式
- beforefieldinit 方式
二者的執(zhí)行差別主要體現(xiàn)在是否為類型實(shí)現(xiàn)了顯式的靜態(tài)構(gòu)造函數(shù),如果實(shí)現(xiàn)了顯式的靜態(tài)構(gòu)造函數(shù),則按照precise 方式執(zhí)行;如果沒(méi)有實(shí)現(xiàn)顯式的靜態(tài)構(gòu)造函數(shù),則按照beforefieldinit 方式執(zhí)行。
為了說(shuō)清楚類型構(gòu)造器的執(zhí)行情況,我們首先在概念上必須明確一個(gè)前提,那就是precise 的語(yǔ)義明確了.cctor 的調(diào)用和調(diào)用存取靜態(tài)成員的時(shí)機(jī)存在精確的關(guān)系,所以換句話說(shuō),類型構(gòu)造器的執(zhí)行時(shí)機(jī)在語(yǔ)義上決定于是否顯式的聲明了靜態(tài)構(gòu)造函數(shù),以及存取靜態(tài)成員的時(shí)機(jī),這兩個(gè)因素。
我們還是從User 類的實(shí)現(xiàn)說(shuō)起,一一過(guò)招分析這兩種方式的執(zhí)行過(guò)程。
3.1 precise 方式
首先實(shí)現(xiàn)顯式的靜態(tài)構(gòu)造函數(shù)方案,為:
對(duì)應(yīng)的IL 代碼為:
為了進(jìn)行對(duì)比分析,我們需要首先分析beforefieldinit 方式的執(zhí)行情況,所以接著繼續(xù)。。。
3.2 beforefieldinit 方式
為User 類型,不實(shí)現(xiàn)顯式的靜態(tài)構(gòu)造函數(shù)方案,為:
3.3 分析差別
從IL 代碼的執(zhí)行過(guò)程而言,我們首先可以了解的是在顯式和隱式實(shí)現(xiàn)類型構(gòu)造函數(shù)的內(nèi)部,除了添加新的初始化操作之外,二者的實(shí)現(xiàn)是基本相同的。所以要找出兩種方式的差別,我們最終將著眼點(diǎn)鎖定在二者元數(shù)據(jù)的聲明上,隱式方式多了一個(gè)稱為beforefieldinit 標(biāo)記的指令。
那么,beforefieldinit 究竟表示什么樣的語(yǔ)義呢?Scott Allen 對(duì)此進(jìn)行了詳細(xì)的解釋:beforefieldinit 為CLR 提供了在任何時(shí)候執(zhí)行.cctor 的授權(quán),只要該方法在第一次訪問(wèn)類型的靜態(tài)字段之前執(zhí)行即可。
所以,如果對(duì)precise 方式和beforefieldinit 方式進(jìn)行比較時(shí),二者的差別就在于是否在元數(shù)據(jù)聲明時(shí)標(biāo)記了beforefieldinit 指令。precise 方式下,CLR 必須在第一次訪問(wèn)該類型的靜態(tài)成員或者實(shí)例成員之前執(zhí)行類型構(gòu)造器,也就是說(shuō)必須剛好在存取靜態(tài)成員或者創(chuàng)建實(shí)例成員之前完成類型構(gòu)造器的調(diào)用;beforefieldinit 方式下,CLR 可以在任何時(shí)候執(zhí)行類型構(gòu)造器,一定程度上實(shí)現(xiàn)了對(duì)執(zhí)行性能的優(yōu)化,因此較precise 方式更加高效。
值得注意的是,當(dāng)有多個(gè)beforefieldinit 構(gòu)造器存在時(shí),CLR 無(wú)法保證這多個(gè)構(gòu)造器之間的執(zhí)行順序,因此我們?cè)趯?shí)際的編碼時(shí)應(yīng)該盡量避免這種情況的發(fā)生。
?
備注:
假設(shè)有下面的一個(gè)類
class MyTest{
public static int Number = 1;
public MyTest()
{
Number++;
}
}
被下面的方法調(diào)用
private void Test(){
var t1 = new MyTest();
t1 = null;
var t2 = new MyTest();
}
這里有三行代碼,下面是不同時(shí)間MyTest.Number值的變化:
時(shí)間 MyTest.Number的值
第一行執(zhí)行前???????????0
第一行執(zhí)行后?????????? 2
第二行執(zhí)行后?????????? 2(即使實(shí)例被置為null,但是靜態(tài)值還是常駐內(nèi)存的)
第三行執(zhí)行后???????????3
posted on 2011-08-23 17:38 cutebear 閱讀(...) 評(píng)論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/bear831204/archive/2011/08/23/2151077.html
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!總結(jié)
以上是生活随笔為你收集整理的.Net的类型构造器-static构造函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【转载】要有梦想-创造卓越的职业生涯
- 下一篇: Windows Phone开发(11):