單例模式(20)

来源:http://www.cnblogs.com/xiaomowang/archive/2017/02/13/6392954.html
-Advertisement-
Play Games

今天我們來講一下單例模式,下麵我們來用winform來做一個簡單的展示,就是點擊一個菜單,彈出另一個窗體(做成父子窗體的形式)。 建一個窗體(父窗體),拖一個MenuStrip,再建一個窗體(子窗體)。 然後: 現在,我們看一下執行結果。 我們可以看到,每次我們點擊一下工具,都會彈出一個新的窗體,我 ...


今天我們來講一下單例模式,下麵我們來用winform來做一個簡單的展示,就是點擊一個菜單,彈出另一個窗體(做成父子窗體的形式)。

建一個窗體(父窗體),拖一個MenuStrip,再建一個窗體(子窗體)。

然後:

 1         private void Form1_Load(object sender, System.EventArgs e)
 2         {
 3             this.IsMdiContainer = true;
 4         }
 5 
 6         private void 工具欄ToolStripMenuItem_Click(object sender, System.EventArgs e)
 7         {
 8             Form2 form2 = new Form2();
 9             form2.MdiParent = this;
10             form2.Show();
11         }

現在,我們看一下執行結果。

我們可以看到,每次我們點擊一下工具,都會彈出一個新的窗體,我們想要的結果是:之彈出一次這個窗體就行。

有些伙伴說,可以用ShowDialog() 啊,在此,說明一下,在父子窗體中,子窗體是不能用ShowDialog()出來的,再退一步講,即便是能用ShowDialog(),但是這是一個阻塞機制,很不靈活。

那麼,為了實現我們想要的結果,我們該如何做呢?

簡單啊,我們只需要修改一下點擊事件里的代碼就可以了。

先聲明一個子窗體的全局變數,然後修改一下代碼:

 1         private Form2 form2;
 2         private void Form1_Load(object sender, System.EventArgs e)
 3         {
 4             this.IsMdiContainer = true;
 5         }
 6 
 7         private void 工具欄ToolStripMenuItem_Click(object sender, System.EventArgs e)
 8         {
 9             if (form2 == null)
10             {
11                 form2 = new Form2();
12                 form2.MdiParent = this;
13                 form2.Show();
14             }
15         }

這樣就可以實現我們想要的結果了,這樣就完了嘛?

這裡存在兩個問題:

1、如果我有很多按鈕,每個按鈕都想彈出這個子窗體,想實現這個結果,需要複製粘貼這些代碼,顯然是很失敗的做法。

2、上述結果,如果我關閉了打開的窗體,我在點擊工具菜單,則不會再彈出窗體來了。(因為關閉窗體後,該窗體僅僅是Disposed了,但對象還不是null,所以判斷是否為null是有一定的問題的。)

針對上述兩個問題,我們來改進一下。就用到今天要講的單例模式了,好,我們來看一下如何實現。

Form2中的代碼

 1     public partial class Form2 : Form
 2     {
 3         //聲明一個靜態的類變數
 4         private static Form2 form2 = null;
 5         //構造方法私有,外部代碼不能直接new來實例化它
 6         private Form2()
 7         {
 8             InitializeComponent();
 9         }
10         //得到類實例的方法,返回值就是本類對象,註意也是靜態的。
11         public static Form2 GetForm()
12         {
13             //當內部的form2是null或者被Dispose過,則new它
14             //並且設計其MdiParent為Form1,此時將實例化的對象存在靜態的變數form2中,以後就可以不用實例化而得到它了
15             if (form2 == null || form2.IsDisposed)
16             {
17                 form2 = new Form2();
18                 form2.MdiParent = Form1.ActiveForm;
19             }
20             return form2;
21         }
22     }

Form1 中的調用

 1     public partial class Form1 : Form
 2     {
 3        
 4         public Form1()
 5         {
 6             InitializeComponent();
 7         }
 8     
 9         private void Form1_Load(object sender, System.EventArgs e)
10         {
11             this.IsMdiContainer = true;
12         }
13         private void 工具欄ToolStripMenuItem_Click(object sender, System.EventArgs e)
14         {
15             Form2.GetForm().Show();
16         }
17     }

這樣,就達到了我們想要的效果了。

我們來總結一下:

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

通常我們剋讓一個全局變數是的一個對象被訪問,但它不能防止你實例化多個對象。最好的辦法就是,讓類自身負責保存它的唯一實例,這個類可以保證沒有其他實例可以被創建,並且他可以提供一個訪問該實例的方法。

好,我們來寫一下單例模式的代碼

 1     class Singleton
 2     {
 3         private static Singleton instance;
 4         //構造方法讓其private,這就堵死了外界利用用new創建次類實例的可能
 5         private Singleton()
 6         {
 7             
 8         }
 9         //此方法是獲得本類實例的唯一全局訪問點
10         public static Singleton GetInstance()
11         {
12             //若實例不存在,則new一個新實例,否則返回已有的實例
13             if (instance==null)
14             {
15                 instance = new Singleton();
16             }
17             return instance;
18         }
19     }

客戶端:

 1        public static void Main()
 2         {
 3             Singleton s1 = Singleton.GetInstance();
 4             Singleton s2 = Singleton.GetInstance();
 5             //比較兩次實例化後對象的結果是否相同
 6             if (s1==s2)
 7             {
 8                 Console.WriteLine("兩個對象是相同的實例。");
 9             }
10             Console.ReadKey();
11         }

另外還有一個問題,如果多線程中,多個線程同時訪問Singleton類,調用GetInstance()方法,會有可能創造多個實例的。所以,我們可以用lock進行處理一下。

好,我們來看用lock處理後的Singleton類

 1     class Singleton
 2     {
 3         private static Singleton instance;
 4         //程式運行時創建一個靜態只讀的進程輔助對象
 5         private static readonly object syncRoot = new object();
 6         //構造方法讓其private,這就堵死了外界利用用new創建次類實例的可能
 7         private Singleton()
 8         {
 9 
10         }
11         //此方法是獲得本類實例的唯一全局訪問點
12         public static Singleton GetInstance()
13         {
14             //在同一個時刻加了鎖的那部分程式只有一個線程可以進入
15             lock (syncRoot)
16             {
17                 //若實例不存在,則new一個新實例,否則返回已有的實例
18                 if (instance == null)
19                 {
20                     instance = new Singleton();
21                 }
22             }
23             return instance;
24         }
25     }

對於上述的代碼,小伙伴們發現了一個問題沒有,就是不管instance是不是為null,都會先加鎖,這勢必會影響性能的,好我們來看一下優化後的:

 1     class Singleton
 2     {
 3         private static Singleton instance;
 4         //程式運行時創建一個靜態只讀的進程輔助對象
 5         private static readonly object syncRoot = new object();
 6         //構造方法讓其private,這就堵死了外界利用用new創建次類實例的可能
 7         private Singleton()
 8         {
 9 
10         }
11         //此方法是獲得本類實例的唯一全局訪問點
12         public static Singleton GetInstance()
13         {
14             //先判斷實例是否存在,如果不存在再加鎖處理
15             if (instance==null)
16             {
17                 //在同一個時刻加了鎖的那部分程式只有一個線程可以進入
18                 lock (syncRoot)
19                 {
20                     //若實例不存在,則new一個新實例,否則返回已有的實例
21                     if (instance == null)
22                     {
23                         instance = new Singleton();
24                     }
25                 } 
26             }
27             return instance;
28         }
29     }

其實再實際應用當中,C#與用功語言運行庫也提供了一種“靜態初始化”方法,這種方法不需要開發人員顯示的編寫線程安全代碼,即可解決多線程環境下他是不安全的問題。

好,下麵我們來看一下“靜態初始化”方法的單例模式

 1     //sealed阻止發生派生,而派生可能會增加實例
 2     public sealed class Singleton
 3     {
 4         //在第一次引用類的任何成員時創建實例,共功與原運行庫負責處理變數初始化。
 5         private static readonly Singleton instance = new Singleton();
 6         private Singleton()
 7         {
 8         }
 9 
10         public static Singleton GetInstance()
11         {
12             return instance;
13         }
14     }

這種靜態初始化的方式是自己被載入時就將自己實例化,所以被形象的成為惡漢式單例類,原先的單例模式處理是要再第一次被引用時,才會將自己實例化,所以被稱為懶漢單例類。

好,單例模式我們就介紹完了,下一篇博文我們講 橋接模式


本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持


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

-Advertisement-
Play Games
更多相關文章
  • 歡迎大家來咨詢海南七星彩打獎系統,系統可以支持手機下註的南方海南,湛江七星彩投註網站系統,也可以出租,出售等渠道,或者定製,可以選擇支持手機下註或者不支持,需要定製的,可以私信,扣扣:1930-1335-70 本截圖只作為演示,如有需要定製,購買,請聯繫客服購買正版授權使用。 會員演示圖: 代理演示 ...
  • 1 import java.util.*; 2 class CalendarTest 3 { 4 /*先輸出提示語句,並接受用戶輸入的年、月。 5 根據用戶輸入的年,先判斷是否是閏年。 6 根據用戶輸入的年份來判斷月的天數。 7 用迴圈計算用戶輸入的年份距1900年1月1日的總天數。 8 用迴圈計算... ...
  • mybatis和hibernate之間的對比。及應用場景的介紹 ...
  • sso單點登錄系統的最全的搭建方法,只要你按我的步驟來,就可以成功的搭建出你的sso單點登錄系統。 ...
  • 定義 觀察者模式(有時又被稱為發佈(publish)-訂閱(Subscribe)模式,在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統(摘自百度百科)。 關鍵詞:發佈-訂閱 為什 ...
  • 1.定義 跨域是指a頁面想獲取b頁面資源,如果a、b頁面的協議、功能變數名稱、埠、子功能變數名稱不同,所進行的訪問行動都是跨域的,而瀏覽器為了安全問題一般都限制了跨域訪問,也就是不允許跨域請求資源。註意:跨域限制訪問,其實是瀏覽器的限制。理解這一點很重要!!! 2.跨域訪問示例 假設有兩個網站,A網站部署在:ht ...
  • 一、封裝: 封裝是實現面向對象程式設計的第一步,封裝就是將數據或函數等集合在一個個的單元中(我們稱之為類)。被封裝的對象通常被稱為抽象數據類型。 封裝的意義: 封裝的意義在於保護或者防止代碼(數據)被我們無意中破壞。在面向對象程式設計中數據被看作是一個中心的元素並且和使用它的函數結合的很密切,從而保 ...
  • 如果一個方法中包含多個布爾類型的參數,一是方法不容易理解,二是調用時容易出錯。 重構前代碼 重構後代碼 重構後,將原來方法改為private防止外部調用,而暴露出命名良好的方法供調用。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...