閒來無事,看看構造函數

記得一年以前在寫代碼的時候問了小夥伴一個問題,我說:「你說若是我在聲明的時候初始化,它會在對象初始化的哪一個階段被賦值?」 小夥伴想了想回答我:「仍是在構造函數裏面給變量初始化吧。」 程序當口,時間比較緊,沒有空去驗證一下這個問題,後來也就慢慢淡忘了。設計模式

這個假期翻開「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運算符返回內存飲用,它指向的內存處於初始化好的形式。
相關文章
相關標籤/搜索