欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解析C#設計模式之單例模式

 更新時間:2020年12月02日 11:06:18   作者:yangyang  
這篇文章主要介紹了C#設計模式之單例模式的相關資料,幫助大家更好的理解和學習c# 設計模式的內(nèi)容,感興趣的朋友可以了解下

  單例模式(Singleton),故名思議就是說在整個應用程序中,某一對象的實例只應該存在一個。比如,一個類加載數(shù)據(jù)庫中的數(shù)據(jù)到內(nèi)存中以提供只讀數(shù)據(jù),這就很適合使用單例模式,因為沒有必要在內(nèi)存中加載多份相同的數(shù)據(jù),另外,有些情況下不允許內(nèi)存中存在多分份相同的數(shù)據(jù),比如數(shù)據(jù)過大,內(nèi)存容不下兩份相同數(shù)據(jù)等等。

約定單例模式(Singleton by Convention)

    這種方式有點“Too simple, Sometimes naïve”,他就是提示使用者,我是單例,不要重復初始化我,比如:

public class Database
{
  /// <summary>
  /// 警告,這是單例,不要初始化多次,否則,后果自負.
  /// </summary>
  public Database() {}
};

 一種情況是,根本不會注意到這個提示,其次是在很多時候,這些初始化是偷偷摸摸無意中發(fā)生的,比如通過反射,通過工廠產(chǎn)生(Activator.CreateInstance),通過注入等等,雖然有一個“約定大于配置”,但是這里不使用。

   單例模式最常見的想法是提供一個全局的,靜態(tài)的對象。

public static class Globals
{
  public static Database Database = new Database();
}

這種方式并沒有很安全。這個并沒有阻止用戶在其他地方new Database,并且,用戶可能并不知道有一個Globals類,里面有個Database單例。

經(jīng)典的實現(xiàn)方式

   唯一的方式阻止用戶實例化對象是將構造函數(shù)變成私有的,并且提供方法或者屬性返回唯一的內(nèi)部對象。

public class Database
{
  private Database() { ... }
  public static Database Instance { get; } = new Database();
}

現(xiàn)在將構造函數(shù)設置為了私有,當然設為私有依舊可以通過反射繼續(xù)調(diào)用,但是這畢竟需要額外操作,這已經(jīng)可以阻止大部分用戶直接實例化了。通過將實例定義為static靜態(tài), 使得其生命周期延長至應用程序運行期間。

延遲初始化

   以上方法線程安全,但是因為是靜態(tài)屬性,在類的所有實例創(chuàng)建之前,或者任何靜態(tài)成員訪問之前就會初始化,并且在每個AppDomain里都只會初始化一次。

   如何實現(xiàn)延遲初始化,即將單例對象的構造推遲到應用程序首次請求該對象進行時,如果應用程序永遠不請求對象,則對象永遠不會構造。在之前,可以使用double check方式。其實要實現(xiàn)正確的double check還是有些問題需要注意,比如上面這個例子,第一版可能這么寫,用一個鎖。

public class Database
{
  private static Database db;
  private static object olock = new object();
  private Database()
  { }

  public static Database GetInstance()
  {
    lock (olock)
    {
      if (db == null)
      {
        db = new Database();
      }
      return db;
    }
  }
}

這雖然線程安全,但是每次訪問GetInstance,不論對象是否已經(jīng)創(chuàng)建,都需要獲取然后釋放鎖,比較消耗資源,所以,在外面再加一層判斷。

public class Database
{
  private static Database db;
  private static object olock = new object();
  private Database()
  { }

  public static Database GetInstance()
  {
    if (db == null)
    {
      lock (olock)
      {
        if (db == null)
        {
          db = new Database();
        }
      }
    }
    return db;
  }
}

在訪問對象之前判斷是否已經(jīng)初始化,如果初始化直接返回,這樣就避免了一次對鎖的訪問。But,這里仍然存在問題。假設Database初始化起來耗時,當線程A獲得鎖正在對db進行初始化的時候,線程B在最外層判斷db是否為空,這個時候,線程A正在初始化db,有可能只初始化了部分,這個時候db就可能不為空,直接返回了沒有完全初始化完全的對象,這可能導致線程B崩潰。

   解決方式是,將對象存儲到臨時變量中,然后以原子寫的方式存儲到db中,如下

public class Database
{
  private static Database db;
  private static object olock = new object();
  private Database()
  { }

  public static Database GetInstance()
  {
    if (db == null)
    {
      lock (olock)
      {
        if (db == null)
        {
          var temp = new Database();
          Volatile.Write(ref db, temp);
        }
      }
    }
    return db;
  }
}

非常繁瑣,雖然實現(xiàn)了延遲初始化,但是跟開頭的靜態(tài)字段對比,復雜太多,而且一不小心就會寫錯。雖然可以簡化為:

public static Database GetInstance()
{
  if (db == null)
  {
    var temp = new Database();
    Interlocked.CompareExchange(ref db, temp, null);
  }
  return db;
}

 該方法看起來沒有用鎖,temp對象有可能會被初始化兩次,但是在將temp寫入到db的時候,Interlock.CompareExchange會保證只會有1個對象正確被寫入到db,沒有被寫入的temp對象會被垃圾回收,這種方式速度比上述的double check要快。但仍然需要學習成本。幸好,C#提供了Lazy方法:

private static Lazy<Database> db = new Lazy<Database>(() => new Database(), true);
public static Database GetInstance() => db.Value;

簡單且完美。

依賴注入與單例模式

   前面的單例模式其實是一種代碼侵入的做法,就是要想一個原本沒有實現(xiàn)單例的代碼要實現(xiàn)單例,需要修改代碼實現(xiàn),并且這個代碼還容易出錯。有些人認為單例模式的唯一正確的做法就是在IOC依賴注入中,這樣不需要修改源代碼,通過依賴注入框架來實現(xiàn)依賴注入,在統(tǒng)一的入口,統(tǒng)一的管理生命周期,在ASP.NET Core MVC中,在Startup的ConfigureServices代碼中:

services.AddSingleton<Database>();

或者加入需要用IDatabase的地方,要用Database單例的話:

services.AddSingleton<IDatabase,Database>();

在ASP.NET Core MVC的后續(xù)代碼中,只要用到IDatabase的地方,就會用Database的單例來實現(xiàn),不需要我們在Database內(nèi)做任何修改。在使用的時候,只需要引用IServiceProvider接口里的GetService方法,IServiceProvider是由ASP.NET Core MVC的IOC框架直接提供,不需要特別處理:

public XXXController(IServiceProvider serviceProvider)
{
   var db = serviceProvider.GetService<IDatabase>();
}

單態(tài)模式(Monostate)

    單態(tài)模式是單例模式的一個變種,它是一個普通的類,但是其行為和表現(xiàn)就像單例模式。

    比如我們在對一個公司的人員結構進行建模,一個典型的公司一般只會有一個CEO,

public class ChiefExecutiveOfficer
{
  private static string name;
  private static int age;

  public string Name
  {
    get => name;
    set => name = value;
  }

  public int Age
  {
    get => age;
    set => age = value;
  }
}

這個類的屬性有get,set,但是其背后的私有字段都是靜態(tài)的。所以不管這個ChiefExecutiveOfficer實例化多少次,其內(nèi)部引用的都是同一個數(shù)據(jù)。比如,可以實例化兩個對象,但是其內(nèi)部的內(nèi)容是一模一樣的。

    單態(tài)模式簡單,但是容易引起混亂,所以要想簡單,要想實現(xiàn)單例效果,最好還是使用IOC這種依賴注入的框架,讓它來幫助我們來管理實例及其生命周期。

以上就是解析C#設計模式之單例模式的詳細內(nèi)容,更多關于c# 單例模式的資料請關注腳本之家其它相關文章!

相關文章

  • C#?JWT權限驗證的實現(xiàn)

    C#?JWT權限驗證的實現(xiàn)

    本文主要介紹了C#?JWT權限驗證的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C# 利用代理爬蟲網(wǎng)頁的實現(xiàn)方法

    C# 利用代理爬蟲網(wǎng)頁的實現(xiàn)方法

    這篇文章主要介紹了C# 利用代理爬網(wǎng)頁的實現(xiàn)方法的相關資料,希望通過本能幫助到大家實現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-10-10
  • C#如何讀取Txt大數(shù)據(jù)并更新到數(shù)據(jù)庫詳解

    C#如何讀取Txt大數(shù)據(jù)并更新到數(shù)據(jù)庫詳解

    這篇文章主要給大家介紹了關于C#如何讀取Txt大數(shù)據(jù)并更新到數(shù)據(jù)庫的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用C#具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-08-08
  • C#不提升自己程序的權限實現(xiàn)操作注冊表

    C#不提升自己程序的權限實現(xiàn)操作注冊表

    這篇文章主要介紹了C#不提升自己程序的權限實現(xiàn)操作注冊表的相關資料,需要的朋友可以參考下
    2022-12-12
  • Unity存儲游戲數(shù)據(jù)的多種方法小結

    Unity存儲游戲數(shù)據(jù)的多種方法小結

    這篇文章主要介紹了Unity存儲游戲數(shù)據(jù)的幾種方法,在游戲開發(fā)中,存儲游戲數(shù)據(jù)是非常重要的,因為游戲數(shù)據(jù)決定了游戲的各個方面,例如游戲的進度、玩家的成就、游戲的設置,需要的朋友可以參考下
    2023-02-02
  • C#構造函數(shù)詳解

    C#構造函數(shù)詳解

    本文詳細講解了C#中的構造函數(shù),文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 如何使用C#將Tensorflow訓練的.pb文件用在生產(chǎn)環(huán)境詳解

    如何使用C#將Tensorflow訓練的.pb文件用在生產(chǎn)環(huán)境詳解

    這篇文章主要給大家介紹了關于如何使用C#將Tensorflow訓練的.pb文件用在生產(chǎn)環(huán)境的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧
    2018-11-11
  • C#?實例解釋面向?qū)ο缶幊讨械膯我还δ茉瓌t(示例代碼)

    C#?實例解釋面向?qū)ο缶幊讨械膯我还δ茉瓌t(示例代碼)

    本文我介紹了?SOLID?原則中的單一功能原則(single-responsibility?principle),并通過?C#?代碼示例簡明地詮釋了它的含意和實現(xiàn),對C#?面向?qū)ο缶幊淘瓌t感興趣的朋友跟隨小編一起看看吧
    2022-02-02
  • C#自定義事件之屬性改變引發(fā)事件示例

    C#自定義事件之屬性改變引發(fā)事件示例

    這篇文章主要為大家詳細介紹了C#自定義事件之屬性改變引發(fā)事件示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • C#中如何使用redis

    C#中如何使用redis

    這篇文章主要介紹了C#中如何使用redis,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07

最新評論