C#中爲什麼會出現空靜態構造方法的寫法

再過幾個小時,就要回家過春節了,今天說些簡單點的東西,大家在看C#代碼的時候,一定會對這樣的寫法非常迷茫:在一個類中會出現一個空的靜態構造方法。這不是多此一舉嗎,這樣做的目的是什麼?今天我就來說說這個內容。

前段時間,小夥伴遇到一個問題,百思不得其解,我先來模擬下這個問題:

 class Program
    {
        static void Main(string[] args)
        {
            //1.初始化配置中心
            Console.WriteLine("初始化配置中心");
           
            //2.利用從配置中心讀取出來的內容進行了一些操作
             String config= MyTest.config;
            //dosomething
        }
    }
    public class MyTest
    {
        public static string config = getConfig();

        private static string getConfig()
        {
            //讀取配置中心的內容,並返回
            return "";
        }
    }

代碼比較簡單,就是有兩個類,一個是主程序入口,一個是業務類,在業務類裏面,定義了一個static的變量,給它賦上一個方法,方法中讀取了配置中心的內容,並且返回,那麼這個static的變量的值就是配置中心的內容了,在主程序入口,一開始就初始化了配置中心,然後訪問在業務類中的靜態變量,並且利用這個值,做一些後續操作。

我們先不管這樣的邏輯是否合理,就只看能否正常運行。

這樣的代碼看上去並沒有什麼問題,但是讓人不解的是,拋出了異常,內容是“配置中心未初始化”,小夥伴懵了,明明一開始就初始了配置中心啊,爲什麼讀取配置中心內容的時候,還會出現這樣的異常呢。

我一看,立刻懂了,於是我在業務類中,加了一個靜態的構造方法,如下所示:

    public class MyTest
    {
        public static string config = getConfig();

        private static string getConfig()
        {
            //讀取配置中心的內容,並返回
            return "";
        }

        static MyTest() { }
    }

一切都好了。

我加了一個空的靜態方法,注意是空的,爲什麼加了一個空的靜態方法可以解決問題呢?我們再來做個試驗把:

    class Program
    {
        static void Main(string[] args)
        {
            //初始化配置中心
            Console.WriteLine("初始化配置中心");

            //2.利用從配置中心讀取出來的內容進行了一些操作
            String config = MyTest.config;
            Console.WriteLine(config);
        }
    }
  public class MyTest
    {
        public static string config = getConfig();

        private static string getConfig()
        {
            Console.WriteLine("進到了getConfig方法");
            //讀取配置中心的內容,並返回
            return "配置中心的內容";
        }
    }

讓我們想想會輸出什麼?這還不簡單,當然是 初始化配置中心 進到了getConfig方法 配置中心的內容,但是,當我們運行:



你會發現,奇怪的事情出現了,第一個輸出的竟然是 進到了“getConfig方法”。

我們爲MyTest類加上一個空的靜態構造方法,再看看:

 public class MyTest
    {
        public static string config = getConfig();

        private static string getConfig()
        {
            Console.WriteLine("進到了getConfig方法");
            //讀取配置中心的內容,並返回
            return "配置中心的內容";
        }

        static MyTest() { }
    }

輸出竟然被改變了。

這就是解釋了爲什麼小夥伴一開始的代碼會出現問題的原因,因爲程序一上來,還沒有執行 初始化配置中心呢,直接讀取了配置中心的內容,而我加上的空靜態構造方法,就改變了代碼的執行順序,是不是很神奇。

我們在用ILSpy看下IL代碼,當類中沒有靜態構造方法的時候:



IL代碼有一個標記:beforefieldinit

當類中的靜態構造方法的時候:



beforefieldinit標記消失了。

我們來做一個總結,當一個類中沒有靜態構造方法的時候,IL會有beforefieldinit標記,程序一運行,就會初始化靜態字段,當一個類中有靜態構造方法的時候,IL沒有beforefieldinit標記,程序一開始就不會初始化靜態字段,而是用到這個類了,才初始化靜態字段。

現在我們可以解釋爲什麼在餓漢式的單例模式中,經常會看到空的靜態構造方法了,因爲不想讓程序在一開始的時候就初始化這個單例對象,而是用到了纔去初始化,相當於懶加載,其實這也是一種優化,如果程序運行後,長時間沒有使用到這個單例對象,而一開始程序就把單例對象加載到內存中去了,也是一種浪費。

這篇的內容到這裏就結束了,哈哈,馬上就解放啦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章