最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c#單例模式(Singleton)的6種實現(xiàn)

 更新時間:2016年12月21日 16:55:38   投稿:zx  
這篇文章主要介紹了c#單例模式(Singleton)的6種實現(xiàn) ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

1.1.1 摘要

 在我們?nèi)粘5墓ぷ髦薪?jīng)常需要在應(yīng)用程序中保持一個唯一的實例,如:IO處理,數(shù)據(jù)庫操作等,由于這些對象都要占用重要的系統(tǒng)資源,所以我們必須限制這些實例的創(chuàng)建或始終使用一個公用的實例,這就是我們今天要介紹的——單例模式(Singleton)。

 使用頻率

單件模式(Singleton):保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。

1.1.2 正文

圖1單例模式(Singleton)結(jié)構(gòu)圖

單例模式(Singleton)是幾個創(chuàng)建模式中最對立的一個,它的主要特點不是根據(jù)用戶程序調(diào)用生成一個新的實例,而是控制某個類型的實例唯一性,通過上圖我們知道它包含的角色只有一個,就是Singleton,它擁有一個私有構(gòu)造函數(shù),這確保用戶無法通過new直接實例它。除此之外,該模式中包含一個靜態(tài)私有成員變量instance與靜態(tài)公有方法Instance()。Instance()方法負責(zé)檢驗并實例化自己,然后存儲在靜態(tài)成員變量中,以確保只有一個實例被創(chuàng)建。

圖2單例模式(Singleton)邏輯模型

接下來我們將介紹6中不同的單例模式(Singleton)的實現(xiàn)方式。這些實現(xiàn)方式都有以下的共同點:

 1.有一個私有的無參構(gòu)造函數(shù),這可以防止其他類實例化它,而且單例類也不應(yīng)該被繼承,如果單例類允許繼承那么每個子類都可以創(chuàng)建實例,這就違背了Singleton模式“唯一實例”的初衷。

2.單例類被定義為sealed,就像前面提到的該類不應(yīng)該被繼承,所以為了保險起見可以把該類定義成不允許派生,但沒有要求一定要這樣定義。

3.一個靜態(tài)的變量用來保存單實例的引用。

4.一個公有的靜態(tài)方法用來獲取單實例的引用,如果實例為null即創(chuàng)建一個。

版本一線程不安全

 /// <summary>
/// A simple singleton class implements.
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;

  /// <summary>
  /// Prevents a default instance of the 
  /// <see cref="Singleton"/> class from being created.
  /// </summary>
  private Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get { return _instance ?? (_instance = new Singleton()); }
  }
}

 以上的實現(xiàn)方式適用于單線程環(huán)境,因為在多線程的環(huán)境下有可能得到Singleton類的多個實例。假如同時有兩個線程去判斷

(null == _singleton),并且得到的結(jié)果為真,那么兩個線程都會創(chuàng)建類Singleton的實例,這樣就違背了Singleton模式“唯一實例”的初衷。

 版本二線程安全

 /// <summary>
/// A thread-safe singleton class.
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;
  private static readonly object SynObject = new object();

  Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get
    {
      // Syn operation.
      lock (SynObject)
      {
        return _instance ?? (_instance = new Singleton());
      }
    }
  }
}

以上方式的實現(xiàn)方式是線程安全的,首先我們創(chuàng)建了一個靜態(tài)只讀的進程輔助對象,由于lock是確保當(dāng)一個線程位于代碼的臨界區(qū)時,另一個線程不能進入臨界區(qū)(同步操作)。如果其他線程試圖進入鎖定的代碼,則它將一直等待,直到該對象被釋放。從而確保在多線程下不會創(chuàng)建多個對象實例了。只是這種實現(xiàn)方式要進行同步操作,這將是影響系統(tǒng)性能的瓶頸和增加了額外的開銷。

 Double-Checked Locking

前面講到的線程安全的實現(xiàn)方式的問題是要進行同步操作,那么我們是否可以降低通過操作的次數(shù)呢?其實我們只需在同步操作之前,添加判斷該實例是否為null就可以降低通過操作的次數(shù)了,這樣是經(jīng)典的Double-Checked Locking方法。

 /// <summary>
/// Double-Checked Locking implements a thread-safe singleton class
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;
  // Creates an syn object.
  private static readonly object SynObject = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      // Double-Checked Locking
      if (null == _instance)
      {
        lock (SynObject)
        {
          if (null == _instance)
          {
            _instance = new Singleton();
          }
        }
      }
      return _instance;
    }
  }
}

 在介紹第四種實現(xiàn)方式之前,首先讓我們認識什么是,當(dāng)字段被標記為beforefieldinit類型時,該字段初始化可以發(fā)生在任何時候任何字段被引用之前。這句話聽起了有點別扭,接下來讓我們通過具體的例子介紹。

 /// <summary>
/// Defines a test class.
/// </summary>
class Test
{
  public static string x = EchoAndReturn("In type initializer");

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

上面我們定義了一個包含靜態(tài)字段和方法的類Test,但要注意我們并沒有定義靜態(tài)的構(gòu)造函數(shù)。

圖3 Test類的IL代碼

class Test
{
  public static string x = EchoAndReturn("In type initializer");

  // Defines a parameterless constructor.
  static Test()
  {
  }

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

上面我們給Test類添加一個靜態(tài)的構(gòu)造函數(shù)。

圖4 Test類的IL代碼

通過上面Test類的IL代碼的區(qū)別我們發(fā)現(xiàn),當(dāng)Test類包含靜態(tài)字段,而且沒有定義靜態(tài)的構(gòu)造函數(shù)時,該類會被標記為beforefieldinit。

現(xiàn)在也許有人會問:“被標記為beforefieldinit和沒有標記的有什么區(qū)別呢”?OK現(xiàn)在讓我們通過下面的具體例子看一下它們的區(qū)別吧!

 class Test
{
  public static string x = EchoAndReturn("In type initializer");

  static Test()
  {
  }

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

class Driver
{
  public static void Main()
  {
    Console.WriteLine("Starting Main");
    // Invoke a static method on Test
    Test.EchoAndReturn("Echo!");
    Console.WriteLine("After echo");
    Console.ReadLine();

    // The output result:
    // Starting Main
    // In type initializer
    // Echo!
    // After echo      
  }
}

我相信大家都可以得到答案,如果在調(diào)用EchoAndReturn()方法之前,需要完成靜態(tài)成員的初始化,所以最終的輸出結(jié)果如下:

圖5輸出結(jié)果

 接著我們在Main()方法中添加string y = Test.x,如下:

public static void Main()
{
  Console.WriteLine("Starting Main");
  // Invoke a static method on Test
  Test.EchoAndReturn("Echo!");
  Console.WriteLine("After echo");

  //Reference a static field in Test
  string y = Test.x;
  //Use the value just to avoid compiler cleverness
  if (y != null)
  {
    Console.WriteLine("After field access");
  }
  Console.ReadKey();

  // The output result:
  // In type initializer
  // Starting Main
  // Echo!
  // After echo
  // After field access

}

圖6 輸出結(jié)果

通過上面的輸出結(jié)果,大家可以發(fā)現(xiàn)靜態(tài)字段的初始化跑到了靜態(tài)方法調(diào)用之前,Wo難以想象?。?/p>

最后我們在Test類中添加一個靜態(tài)構(gòu)造函數(shù)如下:

 class Test
{
  public static string x = EchoAndReturn("In type initializer");

  static Test()
  {
  }

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}
 

圖7 輸出結(jié)果

理論上,type initializer應(yīng)該發(fā)生在”Echo!”之后和”After echo”之前,但這里卻出現(xiàn)了不唯一的結(jié)果,只有當(dāng)Test類包含靜態(tài)構(gòu)造函數(shù)時,才能確保type initializer的初始化發(fā)生在”Echo!”之后和”After echo”之前。

所以說要確保type initializer發(fā)生在被字段引用時,我們應(yīng)該給該類添加靜態(tài)構(gòu)造函數(shù)。接下來讓我們介紹單例模式的靜態(tài)方式。

 靜態(tài)初始化

 

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

  // Explicit static constructor to tell C# compiler
  // not to mark type as beforefieldinit
  static Singleton()
  {
  }

  /// <summary>
  /// Prevents a default instance of the 
  /// <see cref="Singleton"/> class from being created.
  /// </summary>
  private Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get
    {
      return _instance;
    }
  }
}

以上方式實現(xiàn)比之前介紹的方式都要簡單,但它確實是多線程環(huán)境下,C#實現(xiàn)的Singleton的一種方式。由于這種靜態(tài)初始化的方式是在自己的字段被引用時才會實例化。

 讓我們通過IL代碼來分析靜態(tài)初始化。

圖8靜態(tài)初始化IL代碼

首先這里沒有beforefieldinit的修飾符,由于我們添加了靜態(tài)構(gòu)造函數(shù)當(dāng)靜態(tài)字段被引用時才進行初始化,因此即便很多線程試圖引用_instance,也需要等靜態(tài)構(gòu)造函數(shù)執(zhí)行完并把靜態(tài)成員_instance實例化之后可以使用。

 延遲初始化

 /// <summary>
/// Delaies initialization.
/// </summary>
public sealed class Singleton
{
  private Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance { get { return Nested._instance; } }

  private class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

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

這里我們把初始化工作放到Nested類中的一個靜態(tài)成員來完成,這樣就實現(xiàn)了延遲初始化。

 Lazy<T> type

 /// <summary>
/// .NET 4's Lazy<T> type
/// </summary>
public sealed class Singleton
{
  private static readonly Lazy<Singleton> lazy =
    new Lazy<Singleton>(() => new Singleton());

  public static Singleton Instance { get { return lazy.Value; } }

  private Singleton()
  {
  }
}

 這種方式的簡單和性能良好,而且還提供檢查是否已經(jīng)創(chuàng)建實例的屬性IsValueCreated。

 具體例子

現(xiàn)在讓我們使用單例模式(Singleton)實現(xiàn)負載平衡器,首先我們定義一個服務(wù)器類,它包含服務(wù)器名和IP地址如下:

 /// <summary>
/// Represents a server machine
/// </summary>
class Server
{
  // Gets or sets server name
  public string Name { get; set; }

  // Gets or sets server IP address
  public string IP { get; set; }
}

由于負載平衡器只提供一個對象實例供服務(wù)器使用,所以我們使用單例模式(Singleton)實現(xiàn)該負載平衡器。

 /// <summary>
/// The 'Singleton' class
/// </summary>
sealed class LoadBalancer
{
  private static readonly LoadBalancer _instance =
    new LoadBalancer();

  // Type-safe generic list of servers
  private List<Server> _servers;
  private Random _random = new Random();

  static LoadBalancer()
  {
  }

  // Note: constructor is 'private'
  private LoadBalancer()
  {
    // Load list of available servers
    _servers = new List<Server> 
      { 
       new Server{ Name = "ServerI", IP = "192.168.0.108" },
       new Server{ Name = "ServerII", IP = "192.168.0.109" },
       new Server{ Name = "ServerIII", IP = "192.168.0.110" },
       new Server{ Name = "ServerIV", IP = "192.168.0.111" },
       new Server{ Name = "ServerV", IP = "192.168.0.112" },
      };
  }

  /// <summary>
  /// Gets the instance through static initialization.
  /// </summary>
  public static LoadBalancer Instance
  {
    get { return _instance; }
  }


  // Simple, but effective load balancer
  public Server NextServer
  {
    get
    {
      int r = _random.Next(_servers.Count);
      return _servers[r];
    }
  }
}

 上面負載平衡器類LoadBalancer我們使用靜態(tài)初始化方式實現(xiàn)單例模式(Singleton)。

 static void Main()
{
  LoadBalancer b1 = LoadBalancer.Instance;
  b1.GetHashCode();
  LoadBalancer b2 = LoadBalancer.Instance;
  LoadBalancer b3 = LoadBalancer.Instance;
  LoadBalancer b4 = LoadBalancer.Instance;

  // Confirm these are the same instance
  if (b1 == b2 && b2 == b3 && b3 == b4)
  {
    Console.WriteLine("Same instance\n");
  }

  // Next, load balance 15 requests for a server
  LoadBalancer balancer = LoadBalancer.Instance;
  for (int i = 0; i < 15; i++)
  {
    string serverName = balancer.NextServer.Name;
    Console.WriteLine("Dispatch request to: " + serverName);
  }

  Console.ReadKey();
}

圖9 LoadBalancer輸出結(jié)果

 1.1.3 總結(jié)

單例模式的優(yōu)點:

單例模式(Singleton)會控制其實例對象的數(shù)量,從而確保訪問對象的唯一性。

1.實例控制:單例模式防止其它對象對自己的實例化,確保所有的對象都訪問一個實例。

2.伸縮性:因為由類自己來控制實例化進程,類就在改變實例化進程上有相應(yīng)的伸縮性。

 單例模式的缺點:

1.系統(tǒng)開銷。雖然這個系統(tǒng)開銷看起來很小,但是每次引用這個類實例的時候都要進行實例是否存在的檢查。這個問題可以通過靜態(tài)實例來解決。

2.開發(fā)混淆。當(dāng)使用一個單例模式的對象的時候(特別是定義在類庫中的),開發(fā)人員必須要記住不能使用new關(guān)鍵字來實例化對象。因為開發(fā)者看不到在類庫中的源代碼,所以當(dāng)他們發(fā)現(xiàn)不能實例化一個類的時候會很驚訝。

3.對象生命周期。單例模式?jīng)]有提出對象的銷毀。在提供內(nèi)存管理的開發(fā)語言(比如,基于.NetFramework的語言)中,只有單例模式對象自己才能將對象實例銷毀,因為只有它擁有對實例的引用。在各種開發(fā)語言中,比如C++,其它類可以銷毀對象實例,但是這么做將導(dǎo)致單例類內(nèi)部的指針指向不明。 

單例適用性

使用Singleton模式有一個必要條件:在一個系統(tǒng)要求一個類只有一個實例時才應(yīng)當(dāng)使用單例模式。反之,如果一個類可以有幾個實例共存,就不要使用單例模式。

不要使用單例模式存取全局變量。這違背了單例模式的用意,最好放到對應(yīng)類的靜態(tài)成員中。

不要將數(shù)據(jù)庫連接做成單例,因為一個系統(tǒng)可能會與數(shù)據(jù)庫有多個連接,并且在有連接池的情況下,應(yīng)當(dāng)盡可能及時釋放連接。Singleton模式由于使用靜態(tài)成員存儲類實例,所以可能會造成資源無法及時釋放,帶來問題。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C# Entity Framework中的IQueryable和IQueryProvider詳解

    C# Entity Framework中的IQueryable和IQueryProvider詳解

    這篇文章主要介紹了C# Entity Framework中的IQueryable和IQueryProvider詳解,本文使用實例分析這兩個接口的內(nèi)部實現(xiàn),需要的朋友可以參考下
    2015-01-01
  • C#配置文件操作類分享

    C#配置文件操作類分享

    這篇文章主要分享了C#配置文件操作類,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • C#使用Datatable導(dǎo)出Excel

    C#使用Datatable導(dǎo)出Excel

    這篇文章主要為大家詳細介紹了C#使用Datatable導(dǎo)出Excel的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • C#串口通信模塊使用方法示例

    C#串口通信模塊使用方法示例

    這篇文章主要介紹了C#串口通信模塊使用方法示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • C# 中如何使用Thread

    C# 中如何使用Thread

    這篇文章主要介紹了C# 中使用 Thread的方法,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2021-01-01
  • C#實現(xiàn)帶百分比的進度條功能示例

    C#實現(xiàn)帶百分比的進度條功能示例

    這篇文章主要介紹了C#實現(xiàn)帶百分比的進度條功能,分析了帶百分比進度條的功能需求并結(jié)合實例形式給出了具體實現(xiàn)步驟與相關(guān)操作方法,需要的朋友可以參考下
    2017-05-05
  • C#字符串和Acsii碼相互轉(zhuǎn)換

    C#字符串和Acsii碼相互轉(zhuǎn)換

    本文主要介紹了C#字符串和Acsii碼相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • C#向圖片添加水印的兩種不同場景與解決方法

    C#向圖片添加水印的兩種不同場景與解決方法

    這篇文章給大家介紹了兩種大家可能需要的功能,利用C#實現(xiàn)不同需求的向圖片添加水印的方法,文章通過效果圖和示例代碼介紹的很詳細,有需要的朋友們可以參考借鑒。
    2016-09-09
  • C# 創(chuàng)建、部署和調(diào)用WebService簡單示例

    C# 創(chuàng)建、部署和調(diào)用WebService簡單示例

    這篇文章主要為大家詳細介紹了C# 創(chuàng)建、部署和調(diào)用WebService的簡單示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 詳解WPF如何在Panel中實現(xiàn)設(shè)置所有子項間距

    詳解WPF如何在Panel中實現(xiàn)設(shè)置所有子項間距

    這篇文章主要為大家詳細介紹了WPF如何在Panel中實現(xiàn)設(shè)置所有子項間距,本文借鑒了 Qt 中的 Spacing 設(shè)置方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-10-10

最新評論

嘉定区| 色达县| 三穗县| 兰考县| 高唐县| 霍邱县| 广昌县| 扬州市| 霞浦县| 静安区| 定西市| 明水县| 定西市| 东至县| 芦溪县| 宜兰县| 江陵县| 克拉玛依市| 吴堡县| 炉霍县| 灵寿县| 会同县| 遵义县| 石城县| 顺昌县| 儋州市| 阳原县| 衡阳县| 和田县| 奉贤区| 隆林| 云梦县| 且末县| 郑州市| 凌源市| 光山县| 赤水市| 凤阳县| 灵璧县| 蒙自县| 灵宝市|