亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

1.1.1 摘要

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

 使用頻率

單件模式(Singleton):保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。

1.1.2 正文

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

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

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

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

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

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

3.一個(gè)靜態(tài)的變量用來(lái)保存單實(shí)例的引用。

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

版本一線程不安全

 /// <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()); }
  }
}

 以上的實(shí)現(xiàn)方式適用于單線程環(huán)境,因?yàn)樵诙嗑€程的環(huán)境下有可能得到Singleton類的多個(gè)實(shí)例。假如同時(shí)有兩個(gè)線程去判斷

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

 版本二線程安全

 /// <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());
      }
    }
  }
}

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

 Double-Checked Locking

前面講到的線程安全的實(shí)現(xiàn)方式的問(wèn)題是要進(jìn)行同步操作,那么我們是否可以降低通過(guò)操作的次數(shù)呢?其實(shí)我們只需在同步操作之前,添加判斷該實(shí)例是否為null就可以降低通過(guò)操作的次數(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;
    }
  }
}

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

 /// <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;
  }
}

上面我們定義了一個(gè)包含靜態(tài)字段和方法的類Test,但要注意我們并沒(méi)有定義靜態(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類添加一個(gè)靜態(tài)的構(gòu)造函數(shù)。

圖4 Test類的IL代碼

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

現(xiàn)在也許有人會(huì)問(wèn):“被標(biāo)記為beforefieldinit和沒(méi)有標(biāo)記的有什么區(qū)別呢”?OK現(xiàn)在讓我們通過(guò)下面的具體例子看一下它們的區(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é)果

 接著我們?cè)贛ain()方法中添加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é)果

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

最后我們?cè)赥est類中添加一個(gè)靜態(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ù)時(shí),才能確保type initializer的初始化發(fā)生在”Echo!”之后和”After echo”之前。

所以說(shuō)要確保type initializer發(fā)生在被字段引用時(shí),我們應(yīng)該給該類添加靜態(tài)構(gòu)造函數(shù)。接下來(lái)讓我們介紹單例模式的靜態(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;
    }
  }
}

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

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

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

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

 延遲初始化

 /// <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類中的一個(gè)靜態(tài)成員來(lái)完成,這樣就實(shí)現(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()
  {
  }
}

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

 具體例子

現(xiàn)在讓我們使用單例模式(Singleton)實(shí)現(xiàn)負(fù)載平衡器,首先我們定義一個(gè)服務(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; }
}

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

 /// <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];
    }
  }
}

 上面負(fù)載平衡器類LoadBalancer我們使用靜態(tài)初始化方式實(shí)現(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)點(diǎn):

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

1.實(shí)例控制:?jiǎn)卫J椒乐蛊渌鼘?duì)象對(duì)自己的實(shí)例化,確保所有的對(duì)象都訪問(wèn)一個(gè)實(shí)例。

2.伸縮性:因?yàn)橛深愖约簛?lái)控制實(shí)例化進(jìn)程,類就在改變實(shí)例化進(jìn)程上有相應(yīng)的伸縮性。

 單例模式的缺點(diǎn):

1.系統(tǒng)開(kāi)銷。雖然這個(gè)系統(tǒng)開(kāi)銷看起來(lái)很小,但是每次引用這個(gè)類實(shí)例的時(shí)候都要進(jìn)行實(shí)例是否存在的檢查。這個(gè)問(wèn)題可以通過(guò)靜態(tài)實(shí)例來(lái)解決。

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

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

單例適用性

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

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

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

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

相關(guān)文章

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

    C# Entity Framework中的IQueryable和IQueryProvider詳解

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

    C#配置文件操作類分享

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

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

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

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

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

    C# 中如何使用Thread

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論