.NET單例模式-------各種寫法&&驗證


.NET單例模式-------各種寫法&&驗證

 

前言

    單例模式對大家來說都不陌生,也很容易搞懂其原理,本篇文章也不提供單例模式的詳細原理解析,本篇文章的目的是展示在C#中單例模式的各種實現方案(不完全,只是最通用的方式)以及其特點的驗證(是不是真的線程安全,是不是真的延遲初始化?),寫單例模式的文章都很多了,各種語言,但是很多地方都只說:本方式支持多線程、支持延遲初始化等,也有很多也提供為什么支持,下面我對所有大家通常使用的幾種單例模式方案進行講解和驗證!有哪里不對的地方,希望能得到尊敬的讀者們拍磚反饋,覺得好,順帶推薦一下,謝謝。

 

簡單原理解析

    單例模式,目標在於確保一個類僅僅能產生一個實例,並且提供一個全局訪問點,獲取該實例。

   無論哪一種單例模式變種,都離不開制作步驟這個中心。就好像無論哪家雞爪店,其制作雞爪方法都大同小異(都要先拿到雞爪,洗雞爪,弄熟雞爪)。我們單例模式其實一樣,其中心步驟包括:限制外部new出該對象的實例,內部提供該類型的一個唯一對象,提供一個全局訪問點讓外界獲取到該唯一對象的實例進行操作。

   其實就這么簡單,下面我要分析4個主要變種單例模式並且分別進行驗證。

 

准備工作

    我先提供一個大概框架給大家,方便用於測試,也可以不下載,繼續看下去。   測試模板下載

    提供的模板很簡單,只有一個類Person,下面給出要點:

    1.構造函數是私有的(避免new出新實例)。

    2.在構造函數里,我寫的Console.WriteLine(主要是觀察這個類的實例是何時初始化的,初始化了多少次)。

    3.靜態方法getName()的作用是:在還沒有通過全局訪問點獲取實例之前,調用這個getName方法,內部的實例會不會被初始化,如果不會,證明延遲初始化了,如果會,證明沒有延遲初始化。

    下面先給出一個例子,這個例子是餓漢式單例模式。

public class Person
    {
        /*餓漢式單例(線程安全,不支持延遲初始化)*/
 
        //初始化的時候會有反應,應用於延遲初始化的驗證
        private Person() {
            Console.WriteLine("我初始化了");
        }
        private static String name = "Jarvin";
        //內部的唯一實例
        private static Person instance = new Person();
        //全局訪問點,用於獲取唯一實例
        public static Person getInstance()
        {
            return instance;
        }
 
        //實例方法
        public void Say()
        {
            Console.WriteLine("我是{0}",name);
        }
        /*靜態的方法,應用於延遲初始化驗證
         * 如果調用該方法之前還沒初始,延遲初始化
         * 如果調用該方法之前初始化了,沒有延遲初始化
         */
        public static String getName()
        {
            return name;
        }
        
    }

   測試的方式,要點:

    1.測試是否延遲初始化:測試方法是先調用Person.getName(),看結果返回name之前有沒有被初始化來判斷。

    2.測試線程安全:開3個多線程新任務,任務內容是獲取唯一實例,並且調用實例方法。

  下面給出Main方法的代碼:

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Person.getName());
            Console.WriteLine("下面進入多線程模式");
            for (int i = 0; i < 3; i++)
            {
                Task.Factory.StartNew(letPersonSay);
            }
            Console.ReadKey();
        }
        private static void letPersonSay()
        {
            Person emperor = Person.getInstance();
            emperor.Say();
        }
    }

 

            注意:單例模式秀中中出現的以下代碼只是測試需要使用,在正式的使用場景要去掉。

int i=50000000;
while (i > 0)
{ i--; }

 

單例模式秀

   1.餓漢式單例

        初步判斷:不支持延遲初始化,線程安全。

private Person() {
            Console.WriteLine("我初始化了");
        }
        private static String name = "Jarvin";
        private static Person instance = new Person();
        public static Person getInstance()
        {
            int i = 50000000;
            while (i > 0)
            { i--; }
            return instance;
        }

        public void Say()
        {
            Console.WriteLine("我是{0}",name);
        }
        public static String getName()
        {
            return name;
        }

        驗證:

        分析結果:在靜態方法執行之前(Jarvin字符串)先初始化,不支持延遲初始化。然后進入多線程,沒有問題。驗證通過。

 

   2.懶漢式單例

        初步判斷:支持延遲初始化,線程不安全。

private Person() {
            Console.WriteLine("我初始化了");
        }
        private static String name = "Jarvin";
        private static Person instance;
        public static Person getInstance()
        {
            if (instance == null)
            {
                int i=50000000;
                while (i > 0)
                { i--; }
                instance = new Person();
            }
            return instance;
        }

        public void Say()
        {
            Console.WriteLine("我是{0}",name);
        }
        public static String getName()
        {
            return name;
        }

        驗證:

        分析結果:在調用靜態方法之前並沒有先初始化,所以支持延遲初始化。進入多線程以后,有出現兩次初始化,創建了兩個Person類實例,線程不安全。驗證通過。

 

   3.內部類式單例

        初步判斷:支持延遲初始化,線程安全

private Person()
        {
            Console.WriteLine("我初始化了");
        }
        public static Person getInstance()
        {
            return SingleHelper.GetEmperor();
        }
        private class SingleHelper
        {
            private static Person emperor = new Person();
            public static Person GetEmperor()
            {
                int i = 50000000;
                while (i > 0)
                { i--; }
                return emperor;
            }
        }
        private static string name = "Jarvin";

        public void Say()
        {
            Console.WriteLine("我是{0}", name);
        }
        public static String getName()
        {
            return name;
        }

        驗證:

            分析結果:在調用靜態方法之前並沒有先初始化,所以支持延遲初始化。進入多線程以后,只初始化一次,線程安全。驗證通過。

 

   4.雙檢查式單例

        初步判斷:支持延遲初始化,線程安全

private Person()
        {
            Console.WriteLine("我初始化了");
        }
        public static object Flag = new object();
        public static Person me;
        public static Person getInstance()
        {
            if (me == null)
            {
                lock (Flag)
                {
                    if (me == null)
                    {
                        int i = 50000000;
                        while (i > 0)
                        { i--; }
                        me = new Person();
                    }
                }
            }
            return me;
        }
        private static string name = "Jarvin";

        public void Say()
        {
            Console.WriteLine("我是{0}", name);
        }
        public static String getName()
        {
            return name;
        }

 

        驗證:

            分析結果在調用靜態方法之前並沒有先初始化,所以支持延遲初始化。進入多線程以后,只初始化一次,線程安全。驗證通過。

 總結

    其實單例模式非常簡單,聰明的讀者們看到這里應該對大概通過的這四種單例,以及其特性都了解了。見笑啦,下面提供全部測試的源碼,有興趣的可以收藏。

                                   完整Demo下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM