C# 單例模式和窗體的單例打開方法

来源:https://www.cnblogs.com/ModBus/archive/2018/02/27/8477101.html
-Advertisement-
Play Games

第一種最簡單,但沒有考慮線程安全,在多線程時可能會出問題,不過俺從沒看過出錯的現象,表鄙視我…… public class Singleton{ private static Singleton _instance = null; private Singleton(){} public stati ...


第一種最簡單,但沒有考慮線程安全,在多線程時可能會出問題,不過俺從沒看過出錯的現象,表鄙視我……

public class Singleton
{
    private static Singleton _instance = null;
    private Singleton(){}
    public static Singleton CreateInstance()
    {
        if(_instance == null)

        {
            _instance = new Singleton();
        }
        return _instance;
    }
}

第二種考慮了線程安全,不過有點煩,但絕對是正規寫法,經典的一叉 

public class Singleton
{
    private volatile static Singleton _instance = null;
    private static readonly object lockHelper = new object();
    private Singleton(){}
    public static Singleton CreateInstance()
    {
        if(_instance == null)
        {
            lock(lockHelper)
            {
                if(_instance == null)
                     _instance = new Singleton();
            }
        }
        return _instance;
    }
}

第三種可能是C#這樣的高級語言特有的,實在懶得出奇

public class Singleton
{

    private Singleton(){}
    public static readonly Singleton instance = new Singleton();
}  

 

一、 單例(Singleton)模式

單例模式的特點:

  • 單例類只能有一個實例。
  • 單例類必須自己創建自己的唯一實例。
  • 單例類必須給所有其它對象提供這一實例。

單例模式應用:

  • 每台電腦可以有若幹個印表機,但只能有一個Printer Spooler,避免兩個列印作業同時輸出到印表機。
  • 一個具有自動編號主鍵的表可以有多個用戶同時使用,但資料庫中只能有一個地方分配下一個主鍵編號。否則會出現主鍵重覆。


二、 Singleton模式的結構:

Singleton模式包含的角色只有一個,就是Singleton。Singleton擁有一個私有構造函數,確保用戶無法通過new直接實例它。除此之外,該模式中包含一個靜態私有成員變數instance與靜態公有方法Instance()。Instance方法負責檢驗並實例化自己,然後存儲在靜態成員變數中,以確保只有一個實例被創建。(關於線程問題以及C#所特有的Singleton將在後面詳細論述)。


三、 程式舉例:

該程式演示了Singleton的結構,本身不具有任何實際價值。

// Singleton pattern -- Structural example  
using System;

// "Singleton"
class Singleton
{
  // Fields
  private static Singleton instance;

  // Constructor
  protected Singleton() {}

  // Methods
  public static Singleton Instance()
  {
    // Uses "Lazy initialization"
    if( instance == null )
      instance = new Singleton();

    return instance;
  }
}

/// <summary>
/// Client test
/// </summary>
public class Client
{
  public static void Main()
  {
    // Constructor is protected -- cannot use new
    Singleton s1 = Singleton.Instance();
    Singleton s2 = Singleton.Instance();

    if( s1 == s2 )
      Console.WriteLine( "The same instance" );
  }
}



四、 在什麼情形下使用單例模式:

使用Singleton模式有一個必要條件:在一個系統要求一個類只有一個實例時才應當使用單例模式。反過來,如果一個類可以有幾個實例共存,就不要使用單例模式。

註意:

不要使用單例模式存取全局變數。這違背了單例模式的用意,最好放到對應類的靜態成員中。

不要將資料庫連接做成單例,因為一個系統可能會與資料庫有多個連接,並且在有連接池的情況下,應當儘可能及時釋放連接。Singleton模式由於使用靜態成員存儲類實例,所以可能會造成資源無法及時釋放,帶來問題。


五、 Singleton模式在實際系統中的實現

下麵這段Singleton代碼演示了負載均衡對象。在負載均衡模型中,有多台伺服器可提供服務,任務分配器隨機挑選一臺伺服器提供服務,以確保任務均衡(實際情況比這個複雜的多)。這裡,任務分配實例只能有一個,負責挑選伺服器並分配任務。

// Singleton pattern -- Real World example  

using System;
using System.Collections;
using System.Threading;

// "Singleton"
class LoadBalancer
{
  // Fields
  private static LoadBalancer balancer;
  private ArrayList servers = new ArrayList();
  private Random random = new Random();

  // Constructors (protected)
  protected LoadBalancer()
  {
    // List of available servers
    servers.Add( "ServerI" );
    servers.Add( "ServerII" );
    servers.Add( "ServerIII" );
    servers.Add( "ServerIV" );
    servers.Add( "ServerV" );
  }

  // Methods
  public static LoadBalancer GetLoadBalancer()
  {
    // Support multithreaded applications through
    // "Double checked locking" pattern which avoids
    // locking every time the method is invoked
    if( balancer == null )
    {
      // Only one thread can obtain a mutex
      Mutex mutex = new Mutex();
      mutex.WaitOne();

      if( balancer == null )
        balancer = new LoadBalancer();

      mutex.Close();
    }
    return balancer;
  }

  // Properties
  public string Server
  {
    get
    {
      // Simple, but effective random load balancer
      int r = random.Next( servers.Count );
      return servers[ r ].ToString();
    }
  }
}

/// <summary>
/// SingletonApp test
/// </summary>
///
public class SingletonApp
{
  public static void Main( string[] args )
  {
    LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
    LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
    LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
    LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

    // Same instance?
    if( (b1 == b2) && (b2 == b3) && (b3 == b4) )
      Console.WriteLine( "Same instance" );

    // Do the load balancing
    Console.WriteLine( b1.Server );
    Console.WriteLine( b2.Server );
    Console.WriteLine( b3.Server );
    Console.WriteLine( b4.Server );
  }
}



六、 C#中的Singleton模式

C#的獨特語言特性決定了C#擁有實現Singleton模式的獨特方法。這裡不再贅述原因,給出幾個結果:

方法一:

下麵是利用.NET Framework平臺優勢實現Singleton模式的代碼:

sealed class Singleton
{
   private Singleton();
   public static readonly Singleton Instance=new Singleton();
}

這使得代碼減少了許多,同時也解決了線程問題帶來的性能上損失。那麼它又是怎樣工作的呢?

註意到,Singleton類被聲明為sealed,以此保證它自己不會被繼承,其次沒有了Instance的方法,將原來_instance成員變數變成public readonly,併在聲明時被初始化。通過這些改變,我們確實得到了Singleton的模式,原因是在JIT的處理過程中,如果類中的static屬性被任何方法使用時,.NET Framework將對這個屬性進行初始化,於是在初始化Instance屬性的同時Singleton類實例得以創建和裝載。而私有的構造函數和readonly(只讀)保證了Singleton不會被再次實例化,這正是Singleton設計模式的意圖。
(摘自:http://www.cnblogs.com/huqingyu/archive/2004/07/09/22721.aspx )

不過這也帶來了一些問題,比如無法繼承,實例在程式一運行就被初始化,無法實現延遲初始化等。

詳細情況可以參考微軟MSDN文章:《Exploring the Singleton Design Pattern》

方法二:

既然方法一存在問題,我們還有其它辦法。

 

public sealed class Singleton
{
  Singleton()
  {
  }

  public static Singleton GetInstance()
  {
    return Nested.instance;
  }
    
  class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

    internal static readonly Singleton instance = new Singleton();
  }
}

這實現了延遲初始化,並具有很多優勢,當然也存在一些缺點。詳細內容請訪問:《Implementing the Singleton Pattern in C#》。文章包含五種Singleton實現,就模式、線程、效率、延遲初始化等很多方面進行了詳細論述。

 

參考鏈接:http://blog.csdn.net/zhuangzhineng/article/details/3927455

 

 

單 例模式是廣為流傳的設計模式中的一種。本質上,單例模式是一個只允許創建一個實例,並提供對這個實例簡單的訪問途徑的類。一般而言,單例模式在創建實例時 不允許傳遞任何參數-否則不同參數導致不同的實例創建,就會出現問題!(如果同一個實例可以被同參的不同請求所訪問,那麼工廠模式會更適合。)這篇文章只 針對無參創建的請求進行討論。典型的,單例模式的應用往往是延後創建的(created lazily)---只有在第一次被用到的時候才會被創建。在C#中有實現單例模式有很多種方法。我將在這裡一一展現給大家,從最常見的、線程不安全的到 延後創建的、線程安全的、再到簡潔高效的版本。註意在下麵的代碼中,我忽略了所有私有域,因為私有域是預設的類的成員。In many other languages such as Java, there is a different default, and private should be used.
所有的這些實現都有以下四個特征:
    1.只有一個構造函數,而且是私有的,不帶參數的。這是為了防止其他類對其實例化(這和模式本身有衝突)。同時也防止了子類化--如果一個單例能被子類化 一次,就能被子類化兩次,而如果每個子類可以創建一個實例,這與模式本身又產生了衝突。如果你(遇到這樣的情況):只有在運行期才能知道實際的類型,因此 你需要一個父類的單例,你可以使用工廠模式。
  2.類是密封的。這並不是必須的,嚴格的說,即如上一點所說的原因,可以提高JIT(Just-In-Time , 運行時編譯執行的技術)的效率。
    3.一個靜態變數用來保存單例的引用。
    4.一個用以訪問單例引用的公用靜態方法。
註意:所有這些實現都用到一個公用靜態屬性Instance,作為訪問實例的方法。當然都可以替換為方法,這對線程安全和性能都沒有影響。

 

版本1-非線程安全

// Bad code! Do not use!
public sealed class Singleton
{
    static Singleton instance=null;
    Singleton()
     {
    }
    public static Singleton Instance
     {
        get
         {
            if (instance==null)
             {
                instance = new Singleton();
            }
            return instance;
        }
    }
}
 
如 上提示,上面的代碼是非線程安全的。兩個線程可能同時判斷“if (instance==null)”,發現為TRUE,於是都創建了實例,這又違背了單例模式。註意:事實上在表達式反應前實例已經被創建了,記憶體模型並 不保證新的實例的值被其他的線程看到,除非對應的記憶體屏障已經通過了。(CPU越過記憶體屏障後,將刷新自已對存儲器的緩衝狀態,這樣其他線程才能同步自己 的copy)

 

版本2 - 簡單的線程安全

public sealed class Singleton
{
    static Singleton instance=null;
    static readonly object padlock = new object();
    Singleton()
     {
    }
    public static Singleton Instance
     {
        get
         {
            lock (padlock)
             {
                if (instance==null)
                 {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}
這 個實現是線程安全的。線程首先對共用的對象進行鎖定,然後判斷實例是否在之前已經創建。這裡要小心記憶體屏障問題(在鎖定的時候確保所有的讀操作發生在所獲 得之後,在解鎖的時候確保所有的寫操作發生在鎖釋放之前),確保只有一個線程創建了實例(因為在同一時刻只有一個線程可以在執行那段代碼--到了第二個線 程進入的時候,第一個線程已經完成了實例的創建,這樣表達式返回false)。不幸的是,由於在每次訪問的時候都要進行鎖定,所以影響了性能。(這對於多 線程併發的高性能要求的應用顯得尤為重要)。註意有些該版本的實現對typeof(Singleton)進行鎖定,但我是對類的私有靜態變數進行鎖定。對 那些可被其他類訪問的對象進行鎖定或對類型進行鎖定會導致性能問題甚至引起死鎖。這是一個地雷-任何地方都有可能發生,只有對本就為鎖定而創建的對象進行 鎖定或是那些因為某些目的而被鎖定的文檔才是安全的。通常這些對象都是私有的。這樣有助於寫出線程安全的程式。

 

版本3 -- 用雙重檢測機制的線程安全

// Bad code! Do not use!
public sealed class Singleton
{
    static Singleton instance=null;
    static readonly object padlock = new object();
    Singleton()
     {
    }
    public static Singleton Instance
     {
        get
         {
            if (instance==null)
             {
                lock (padlock)
                 {
                    if (instance==null)
                     {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

 

C#單例模式的幾種實現方式

 

文章總結自張波老師的視頻教程

單例模式

  • 動機(Motivation)

    • 在軟體系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。
    • 如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?
    • 這應該是類設計者的責任,而不是使用者的責任
  • 意圖(Intent)

    • 保證一個類僅有一個實例,並提供一個該實例的全局訪問點。——《設計模式》GoF

簡單實現

public sealed class Singleton
{
    private static Singleton _instance = null;

    private Singleton()
    {

    }

    public static Singleton Instance
    {
        get { return _instance ?? (_instance = new Singleton()); }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

說明:

  • 對於線程來說不安全
  • 單線程中已滿足要求
  • 優點: 
    • 由於實例是在 Instance 屬性方法內部創建的,因此類可以使用附加功能
    • 直到對象要求產生一個實例才執行實例化;這種方法稱為“惰性實例化”。惰性實例化避免了在應用程式啟動時實例化不必要的 singleton。

線程安全的

public sealed class Singleton
{
    private static Singleton _instance = null;
    private static readonly object Padlock = new object();

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock (Padlock)
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }

            }
            return _instance;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

說明:

  • 同一個時刻加了鎖的那部分程式只有一個線程可以進入
  • 對象實例由最先進入的那個線程創建
  • 後來的線程在進入時(instence == null)為假,不會再去創建對象實例
  • 增加了額外的開銷,損失了性能

雙重鎖定

public sealed class Singleton
{
    private static Singleton _instance = null;
    private static readonly object Padlock = new object();

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (Padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton();
                    }

                }
            }

            return _instance;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

說明:

  • 多線程安全
  • 線程不是每次都加鎖
  • 允許實例化延遲到第一次訪問對象時發生

靜態初始化

public sealed class Singleton
{
    private static readonly Singleton _instance = null;

    static Singleton()
    {
        _instance = new Singleton();
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return _instance;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

說明:

  • 依賴公共語言運行庫負責處理變數初始化
  • 公共靜態屬性為訪問實例提供了一個全局訪問點
  • 對實例化機制的控制權較少(.NET代為實現)
  • 靜態初始化是在 .NET 中實現 Singleton 的首選方法

延遲初始化

public sealed class Singleton
{
    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    private class Nested
    {
        internal static readonly Singleton instance = null;
        static Nested()
        {
            instance = new Singleton();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

說明:

  • 初始化工作由Nested類的一個靜態成員來完成,這樣就實現了延遲初始化

註意事項

  • Singleton模式中的實例構造器可以設置為protected以允許子類派生。
  • Singleton模式一般不要支持ICloneable介面,因為這可能會導致多個對象實例,與Singleton模式的初衷違背。
  • Singleton模式一般不要支持序列化,因為這也有可能導致多個對象實例,同樣與Singleton模式的初衷違背。
  • Singletom模式只考慮到了對象創建的管理,沒有考慮對象銷毀的管理。就支持垃圾回收的平臺和對象的開銷來講,我們一般沒有必要對其銷 毀進行特殊的管理。

總結

  • Singleton模式是限制而不是改進類的創建。
  • 理解和擴展Singleton模式的核心是“如何控制用戶使用new對一個類的構造器的任意調用”。
  • 可以很簡單的修改一個Singleton,使它有少數幾個實例,這樣做是允許的而且是有意義的。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.創建excel方法 /// <summary> /// 創建Excel表格 /// </summary> /// <param name="dt">數據流</param> /// <param name="FileName">文件名稱</param> public static void Cre ...
  • 前兩天,當我還在老家收拾行旅,準備回廣州,為IT連的創業再戰365天時,有網友扣上問:Taurus.MVC中如何實現認證和許可權控制,最好能做個小例子。我一不小心回了句:等回廣州我再寫篇文章......然後,今天就來補文章了〜〜〜〜 ...
  • Ocelot(https://github.com/TomPallister/Ocelot)是一個用.net core實現的API網關,Butterfly(https://github.com/ButterflyAPM/butterfly)是用.net core實現的全程式跟蹤,現在,Ocelot中... ...
  • 做過單元測試的同學大概都知道以上幾種測試框架,但我一直很好奇它們到底有什麼不同,然後搜到了一篇不錯的文章清楚地解釋了這幾種框架的最大不同之處。 地址在這裡:http://www.tuicool.com/articles/F3eEn2j 簡而言之,三者是非常相似的,如下所示: 上面是NUnit的,XN ...
  • IIS連接數 常識: 虛擬主機會限制IIS連接數,關於其含義,差不多每個主機供應商都有一套自己的說法,微軟也沒有給出很明確的解釋; 含義: IIS伺服器可以同時容納客戶請求的最高連接數,準確的說應該叫“IIS限制連接數”; 一次連接: 1.網站html請求,html中的圖片資源,html中的腳本資源 ...
  • 1、.Net工程的Properties文件夾下自動生成一個名為AssemblyInfo.cs的文件,一般情況下我們很少直接改動該文件。但我們實際上通過另一個形式操作該文件。那就是通過在滑鼠右鍵點擊項目的屬性進入“應用程式”->“程式集信息”,然後修改信息。 2、通過特性(Attribute)來設置程 ...
  • 剛寫完自定義頭像模塊,記錄一下剛纔的過程,直接上代碼: 在講位元組轉化為string時,網上很多資料都是 string str = System.Text.Encoding.UTF8.GetString(imgByte); 但是親測返回值是亂碼,上傳的PHP時無法解析,所以嘗試使用Convert.To ...
  • 1.Bytes >StringSystem.Text.Encoding.Unicode.GetString(bytes, 0, bytes.Length) 2.String >BytesSystem.Text.Encoding.Unicode.GetBytes(str) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...