欧美大屁股bbbbxxxx,狼人大香伊蕉国产www亚洲,男ji大巴进入女人的视频小说,男人把ji大巴放进女人免费视频,免费情侣作爱视频

歡迎來到入門教程網!

C#教程

當前位置:主頁 > 軟件編程 > C#教程 >

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

來源:本站原創(chuàng)|時間:2020-01-10|欄目:C#教程|點擊: 次

1.1.1 摘要

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

 使用頻率高

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

1.1.2 正文

圖1單例模式(Singleton)結構圖

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

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

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

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

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

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),并且得到的結果為真,那么兩個線程都會創(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是確保當一個線程位于代碼的臨界區(qū)時,另一個線程不能進入臨界區(qū)(同步操作)。如果其他線程試圖進入鎖定的代碼,則它將一直等待,直到該對象被釋放。從而確保在多線程下不會創(chuàng)建多個對象實例了。只是這種實現(xiàn)方式要進行同步操作,這將是影響系統(tǒng)性能的瓶頸和增加了額外的開銷。

 Double-Checked Locking

前面講到的線程安全的實現(xiàn)方式的問題是要進行同步操作,那么我們是否可以降低通過操作的次數(shù)呢?其實我們只需在同步操作之前,添加判斷該實例是否為null就可以降低通過操作的次數(shù)了,這樣是經典的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)方式之前,首先讓我們認識什么是,當字段被標記為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)的構造函數(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)的構造函數(shù)。

圖4 Test類的IL代碼

通過上面Test類的IL代碼的區(qū)別我們發(fā)現(xiàn),當Test類包含靜態(tài)字段,而且沒有定義靜態(tài)的構造函數(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      
  }
}

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

圖5輸出結果

 接著我們在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 輸出結果

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

最后我們在Test類中添加一個靜態(tài)構造函數(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 輸出結果

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

所以說要確保type initializer發(fā)生在被字段引用時,我們應該給該類添加靜態(tài)構造函數(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)構造函數(shù)當靜態(tài)字段被引用時才進行初始化,因此即便很多線程試圖引用_instance,也需要等靜態(tài)構造函數(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()
  {
  }
}

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

 具體例子

現(xiàn)在讓我們使用單例模式(Singleton)實現(xiàn)負載平衡器,首先我們定義一個服務器類,它包含服務器名和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; }
}

由于負載平衡器只提供一個對象實例供服務器使用,所以我們使用單例模式(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輸出結果

 1.1.3 總結

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

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

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

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

 單例模式的缺點:

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

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

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

單例適用性

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

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

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持我們。

上一篇:C#判斷字符串是否是數(shù)字(實例)

欄    目:C#教程

下一篇:C#漢字轉拼音實例(支持多音字)

本文標題:c#單例模式(Singleton)的6種實現(xiàn)

本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/6070.html

網頁制作CMS教程網絡編程軟件編程腳本語言數(shù)據(jù)庫服務器

如果侵犯了您的權利,請與我們聯(lián)系,我們將在24小時內進行處理、任何非本站因素導致的法律后果,本站均不負任何責任。

聯(lián)系QQ:835971066 | 郵箱:835971066#qq.com(#換成@)

Copyright © 2002-2020 腳本教程網 版權所有